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

3
我在我的数据库中有两个表通过外键连接:Page(PageId,其他数据)和PageTag(PageId,Tag)。我使用LINQ为这些表生成了类,其中页面是父项,标签是子集合(一对多关系)。有没有办法从Page类中标记要从数据库中删除的PageTag记录?
快速澄清:
我希望在父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.

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

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方法。但是我在页面类中似乎无法访问DataContext类。

有没有人知道如何在页面类中标记要从数据库中删除的子元素?

7个回答

6
经过进一步研究,我相信我已经找到了一个解决方案。当从集合中移除对象时将其标记为删除由关联属性的DeleteOnNull参数控制。
当两个表之间的关系标记为OnDelete Cascade时,该参数设置为true。
不幸的是,没有办法从设计器内设置此属性,也没有办法从*DataContext.cs文件中的局部类中设置它。如果不启用级联删除,则唯一的设置方法是手动编辑*DataContext.designer.cs文件。
在我的情况下,这意味着找到Page关联并添加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 和一对多关系
Dave Brace -- LINQ to SQL:DeleteOnNull

Aaron,感谢你的见解。但是在您的代码库中编写两行代码来标记要删除的记录不是更容易吗? - Robert Harvey
它在代码库中不会变成两行代码。我可以向存储库添加UpdateTags(Page page,string tagString)方法,但这不会与模型绑定清晰地配合使用。 - AaronSieb
这也很容易设置,现在我知道它与On Delete Cascade相关联,可以避免手动编辑设计师文件。 - AaronSieb

1

抱歉,我的错。那样做行不通。

看起来你需要在你的存储库中完成这个任务,而不是在你的页面类中。在存储库中,你可以访问到原始数据上下文。

虽然有一种方法可以“附加”原始数据上下文,但当你这样做时,它已经成为了一个很臭的代码。


感谢您的输入。看起来LINQ无法处理这种类型的结构 :/ - AaronSieb

0

Aaron,

显然你需要循环遍历你的PageTag记录,并为每个记录调用DeleteOnSubmit。当你调用SubmitChanges时,Linq to SQL应该创建一个聚合查询来一次性删除所有记录,因此开销应该是最小的。

替换

PageTags.Clear();

使用

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

我该如何在我的Page类中访问DataContext? - AaronSieb
在您的PageTag部分类中添加一个DataContext成员。partial class PageTag { DataClassesDataContext myDataContext = new DataClassesDataContext(); public string TagString {..等等。 - Robert Harvey

0

Aaron:

在你的 PageTag 部分类中添加一个 DataContext 成员。

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

    public string TagString { 

..等等。


这不会是一个独立的DataContext吗?它与用于检索PageTag的父DataContext不同。我认为当我在parent DataContext上调用SubmitChanges时,删除不会发生。 - AaronSieb
它会工作。将DataContext视为与数据库的另一个连接即可。之所以看起来很奇怪,是因为从架构的角度来看,您可能应该在PageTag局部类之外操纵您的PageTag对象,而不是在里面进行操纵。 - Robert Harvey
当我尝试从第二个DataConnection中删除实体时,我收到了异常“无法删除未附加的实体”。这似乎不能满足延迟更改直到原始DataContext提交的要求。 - AaronSieb
此问题的答案中发布了代码示例。 - AaronSieb
Aaron,你应该能够通过你的对象引用原始数据上下文,就像这样:tag.dataContext(或者你原始的数据上下文变量叫什么)。 - Robert Harvey

0

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

0
你的 Linq to SQL 实体图中,是否存在将 Page 和 PageTags 表联系起来的关系?如果没有,那就是为什么你无法从 Page 类中看到 PageTags 类。
如果在 PageTags 数据库表中外键设置为允许空值,当你将表拖入设计器时,Linq to SQL 将不会创建链接,即使你在 SQL Server 上创建了关系。

关联已经建立,我可以通过程序访问子集合。问题是对集合所做的更改(特别是对象的删除)没有持久化到数据库中。 - AaronSieb
愚蠢的问题,但是你是否提交了更改? - Robert Harvey
是的。当我提交更改时,会出现重复键错误(因为新标签与旧标签重叠)。 - AaronSieb

0

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

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

由于Tags是Page对象的属性,只要它附加到DataContext,对该集合的任何更改都将适当地触发响应Remove或Add调用的数据库中的删除或插入。


我并不希望在PageTag从集合中移除时立即删除它。我希望在父DataContext调用SubmitChanges()时将其删除。PageTagString属性的更新语义与对象的任何其他数据属性都没有区别。 - AaronSieb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接