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

问题描述:

我有两个表在我的数据库连接的外键:Page(PageId,其他数据)和PageTag(PageId,标签)。我已经使用LINQ为这些表生成类,其中父页为父页,标记为子集(一对多关系)。有没有什么办法在页面类中标记PageTag记录以便从数据库中删除?如何从LINQ to SQL中的子集合中删除记录?

快速Clearification:

我想要当父DataContext的调用的SubmitChanges(),而不是之前被删除的子对象。我希望TagString的行为与Page对象的任何其他属性完全相同。

我想能够像下面的代码:

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. 

这里是我的详细情况:
为了使与标签的收集工作更容易,我添加了一个属性设置即把标签集合为一个字符串Page对象:

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类。

有没有人知道一种方法来标记子元素从页面类中的数据库中删除?

经过一些更多的研究,我相信我设法找到了解决方案。从集合中删除对象时删除标记由Association属性的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

在Linq to SQL实体关系图中,您是否有关系链接Page和PageTags表?如果你不这样做,那就是为什么你不能从Page类中看到PageTags类。

如果PageTags数据库表中的外键被设置为Allow Nulls,即使您在SQL Server上创建了关系,Linq to SQL也不会在将表拖入设计器时创建链接。

+0

该协会是在地方,我可以通过编程访问子集。问题是对集合所做的更改(特别是删除对象)不会持久保存到数据库。 – AaronSieb 2009-05-23 20:23:45

+0

愚蠢的问题,但你提交更改? – 2009-05-23 21:05:41

+0

是的。当我提交更改时,我得到一个重复键错误(因为新标签重叠旧标签)。 – AaronSieb 2009-05-23 21:56:46

这是OR映射可能会变得毛茸茸的地方之一。提供这个TagString属性使事情变得更方便一些,但是从长远来看,它会混淆有人使用TagString属性时真正发生的事情。通过隐藏您执行数据修改的事实,某人可以很容易地在DataContext的范围内来访问并设置TagString而不使用Page实体,这可能会导致一些难以发现的错误。

更好的解决方案是使用L2S模型设计器在Page类上添加Tags属性,并要求在DataContext的范围内直接在Tags属性上编辑PageTags。使TagString属性为只读,因此可以进行类型化(并且仍然提供一些便利),但是可以消除设置该属性的困惑和困难。这种改变澄清了意图,并明确了正在发生的事情以及Page对象的消费者所需要做的事情。

由于Tags是Page对象的一个​​属性,只要它连接到DataContext,对该集合的任何更改都会正确触发数据库中的删除或插入操作,以响应Remove或Add calls。

+0

我不希望在从集合中删除PageTag时立即删除PageTag。我期待它在父DataContext调用SubmitChanges()时被删除。 PageTagString属性的更新语义不打算与对象的任何其他数据属性不同。 – AaronSieb 2009-05-23 20:28:56

亚伦,

显然,你必须循环通过你的PageTag记录,调用DeleteOnSubmit为每一个。当您调用SubmitChanges时,Linq to SQL应该创建一个聚合查询来一次删除所有记录,因此开销应该很小。

foreach (PageTag tag in PageTags) 
    myDataContext.DeleteOnSubmit(tag); 
+0

如何从我的Page类中访问DataContext? – AaronSieb 2009-05-23 21:34:58

+0

将DataContext成员添加到您的PageTag部分类中。 部分类PageTag DataClassesDataContext myDataContext = new DataClassesDataContext(); public string TagString { ..等等。 – 2009-05-23 22:55:27

亚伦更换

PageTags.Clear(); 

一个DataContext成员添加到您的PageTag局部类。

partial class PageTag 
{ 
    DataClassesDataContext myDataContext = new DataClassesDataContext(); 

    public string TagString { 

..等等。张贴在罗伯特·哈维的要求

较大的代码示例:

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 }); 
} 

对不起,我的错。这是行不通的。

看起来你确实需要在你的仓库中做这件事,而不是在你的Page类中。在那里,您可以访问您的原始数据上下文。

有一种方法可以“附加”原始数据上下文,但是到那时候,它已经变得相当的代码味道。