代码之家  ›  专栏  ›  技术社区  ›  AaronSieb

如何从Linq to SQL的子集合中删除记录?

  •  2
  • AaronSieb  · 技术社区  · 16 年前

    我的数据库中有两个表通过外键连接:page(pageid,其他数据)和page tag(pageid,tag)。我使用Linq为这些表生成类,其中页作为父级,标记作为子集合(一对多关系)。是否有任何方法可以将pagetag记录标记为从page类内的数据库中删除?

    快速清理:

    我希望在父DataContext调用SubmitChanges()时删除子对象,而不是之前。我希望标记字符串的行为与页面对象的任何其他属性完全相同。

    我想启用如下代码:

    Page page = mDataContext.Pages.Where(page => page.pageId = 1);
    page.TagString = "new set of tags";
    
    //Changes have not been written to the database at this point.
    
    mDataContext.SubmitChanges();
    
    //All changes should now be saved to the database.
    

    以下是我的详细情况:
    为了使处理标记集合更容易,我向将标记集合视为字符串的页面对象添加了一个属性:

    public string TagString {
        get {
            StringBuilder output = new StringBuilder();
            foreach (PageTag tag in PageTags) {
                output.Append(tag.Tag + " ");
            }
    
            if (output.Length > 0) {
                output.Remove(output.Length - 1, 1);
            }
    
            return output.ToString();
        }
        set {
            string[] tags = value.Split(' ');
            PageTags.Clear();
            foreach (string tag in tags) {
                PageTag pageTag = new PageTag();
                pageTag.Tag = tag;
                PageTags.Add(pageTag);
            }
        }
    }
    

    基本上,其思想是,当一系列标记发送到此属性时,对象的当前标记将被删除,并在其位置生成一个新的集合。

    我遇到的问题是这句话:

    PageTags.Clear();
    

    提交更改时不会从数据库中删除旧标记。

    环顾四周,删除内容的“正确”方法似乎是调用数据上下文类的deleteonsubmit方法。但我似乎没有从Page类中访问DataContext类的权限。

    有人知道如何将子元素标记为从页类内的数据库中删除吗?

    7 回复  |  直到 16 年前
        1
  •  6
  •   AaronSieb    16 年前

    经过更多的研究,我相信我已经找到了解决办法。当对象从集合中移除时,将其标记为删除由关联属性的deleteonnull参数控制。

    当两个表之间的关系标记为OnDelete Cascade时,此参数设置为true。

    不幸的是,无法从设计器中设置此属性,也无法从*dataContext.cs文件的分部类中设置此属性。在不启用级联删除的情况下设置它的唯一方法是手动编辑*dataContext.designer.cs文件。

    在我的例子中,这意味着要查找页面关联,并添加deleteonnull属性:

    [Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)]
    public Page Page
    {
        ...
    }
    

    并添加deleteonnull属性:

    [Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
    public Page Page
    {
        ...
    }
    

    注意,需要将属性添加到pagetag类的page属性中,而不是相反。

    参见:
    Beth Massi -- LINQ to SQL and One-To-Many Relationships
    Dave Brace -- LINQ to SQL: DeleteOnNull

        2
  •  1
  •   Robert Harvey    16 年前

    对不起,我的错。那不行。

    看起来您需要在存储库中完成这项工作,而不是在页面类中。在这里,您可以访问原始数据上下文。

    有一种方法可以“附加”原始数据上下文,但是当您这样做时,它已经变成了代码的味道。

        3
  •  0
  •   Robert Harvey    16 年前

    在您的LINQ to SQL实体图中,您有链接页和页标记表的关系吗?如果不这样做,那就是为什么在page类中看不到pagetags类的原因。

    如果pagetags数据库表中的外键设置为允许空值,则即使在SQL Server上创建了关系,当您将表拖到设计器中时,linq to sql也不会创建链接。

        4
  •  0
  •   jrista    16 年前

    这是其中一个区域,或映射可能会变得有点毛茸茸的。提供这个tagstring属性使事情变得更加方便,但从长远来看,它混淆了当某人使用tagstring属性时实际发生的事情。通过隐藏您正在执行数据修改的事实,可以很容易地有人来设置标记字符串,而不必在DataContext范围内使用页面实体,这可能会导致一些难以发现的错误。

    更好的解决方案是使用L2S模型设计器在页类上添加tags属性,并要求直接在DataContext范围内的tags属性上编辑page tags。将Tagstring属性设置为只读,这样就可以对其进行genreated(并且仍然提供一些便利),但是可以消除设置该属性的混淆和困难。这种更改澄清了意图,并使发生的事情和页面对象的使用者需要做的事情变得明显。

    由于标记是页面对象的属性,只要它附加到DataContext,对该集合的任何更改都将正确触发数据库中的删除或插入,以响应删除或添加调用。

        5
  •  0
  •   Robert Harvey    16 年前

    亚伦

    显然,您必须循环浏览页面标签记录,并为每个记录调用deleteonsubmit。当调用SubmitChanges时,Linq to SQL应该创建一个聚合查询来同时删除所有记录,因此开销应该最小。

    代替

    PageTags.Clear();
    

    具有

    foreach (PageTag tag in PageTags)
        myDataContext.DeleteOnSubmit(tag);
    
        6
  •  0
  •   Robert Harvey    16 年前

    亚伦:

    将DataContext成员添加到PageTag分部类中。

    partial class PageTag 
    { 
        DataClassesDataContext myDataContext = new DataClassesDataContext(); 
    
        public string TagString { 
    

    ..等

        7
  •  0
  •   AaronSieb    16 年前

    应Robert Harvey的要求发布更大的代码示例:

    dataContext.cs文件:

    namespace MyProject.Library.Model
    {
        using Tome.Library.Parsing;
        using System.Text;
    
        partial class Page
        {
            //Part of Robert Harvey's proposed solution.
            MyDataContext mDataContext = new TomeDataContext();
    
            public string TagString {
                get {
                    StringBuilder output = new StringBuilder();
                    foreach (PageTag tag in PageTags) {
                        output.Append(tag.Tag + " ");
                    }
    
                    if (output.Length > 0) {
                        output.Remove(output.Length - 1, 1);
                    }
    
                    return output.ToString();
                }
                set {
                    string[] tags = value.Split(' ');
                    //Original code, fails to mark for deletion.
                    //PageTags.Clear();
    
                    //Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached."
                    foreach (PageTag tag in PageTags) {
                        mDataContext.PageTags.DeleteOnSubmit(tag);
                    }
    
                    foreach (string tag in tags) {
                        PageTag PageTag = new PageTag();
                        PageTag.Tag = tag;
                        PageTags.Add(PageTag);
                    }
                }
            }
    
            private bool mIsNew;
            public bool IsNew {
                get {
                    return mIsNew;
                }
            }
    
            partial void OnCreated() {
                mIsNew = true;
            }
    
            partial void OnLoaded() {
                mIsNew = false;
            }
        }
    }
    

    存储库方法:

    public void Save() {
        mDataContext.SubmitChanges();
    }
    
    public Page GetPage(string pageName) {
        Page page =
            (from p in mDataContext.Pages
            where p.FileName == pageName
            select p).SingleOrDefault();
    
        return page;
    }
    

    用途:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(string pageName, FormCollection formValues) {
        Page updatedPage = mRepository.GetPage(pageName);
    
        //TagString is a Form value, and is set via UpdateModel.
        UpdateModel(updatedPage, formValues.ToValueProvider());
        updatedPage.FileName = pageName;
    
        //At this point NO changes should have been written to the database.
    
        mRepository.Save();
    
        //All changes should NOW be saved to the database.
    
        return RedirectToAction("Index", "Pages", new { PageName = pageName });
    }