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

如何从LINQ to SQL中的子集合中删除记录?,linq,linq-to-sql,Linq,Linq To Sql,我的数据库中有两个表通过外键连接:Page(PageId,other data)和PageTag(PageId,Tag)。我使用LINQ为这些表生成类,页面作为父集合,标记作为子集合(一对多关系)。是否有任何方法可以标记PageTag记录,以便从Page类中从数据库中删除 快速清理: 我希望在父DataContext调用SubmitChanges()时删除子对象,而不是在之前。我希望TagString的行为与Page对象的任何其他属性完全相同 我希望启用如下代码: Page page = mDa

我的数据库中有两个表通过外键连接:Page(PageId,other data)和PageTag(PageId,Tag)。我使用LINQ为这些表生成类,页面作为父集合,标记作为子集合(一对多关系)。是否有任何方法可以标记PageTag记录,以便从Page类中从数据库中删除

快速清理:

我希望在父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类


有人知道从Page类中标记要从数据库中删除的子元素的方法吗?

在Linq到SQL实体图中,是否有链接Page和PageTags表的关系?如果没有,这就是为什么无法从Page类中看到PageTags类


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

这是OR映射可能出现问题的领域之一。提供这个TagString属性使事情变得更加方便,但从长远来看,它混淆了当有人使用TagString属性时实际发生的事情。通过隐藏您执行数据修改的事实,有人可以很容易地出现并设置标记字符串,而无需在DataContext范围内使用您的页面实体,这可能会导致一些难以发现的错误

更好的解决方案是使用L2S模型设计器在Page类上添加Tags属性,并要求在DataContext的范围内直接在Tags属性上编辑PageTags。将TagString属性设置为只读,这样就可以创建它(并且仍然提供了一些便利),但是消除了设置该属性时的混乱和困难。这种更改澄清了意图,并使发生的事情以及页面对象的消费者需要什么来实现它变得显而易见

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

显然,您必须循环遍历PageTag记录,为每个记录调用DeleteOnSubmit。LINQtoSQL应该创建一个聚合查询,以便在调用SubmitChanges时一次删除所有记录,所以开销应该最小

替换

PageTags.Clear();

亚伦:


将DataContext成员添加到PageTag分部类

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

    public string TagString { 

……等等

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

对不起,我的错。那不行

看起来确实需要在存储库中而不是在页面类中执行此操作。在那里,您可以访问原始数据上下文


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

经过进一步的研究,我相信我已经找到了解决方案。将对象从集合中删除时标记为删除由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属性中,而不是相反

另请参见:


关联已就绪,我可以通过编程方式访问子集合。问题是对集合所做的更改(特别是删除对象)不会持久化到数据库中。这是一个愚蠢的问题,但您是否正在提交更改?是的。当我提交更改时,会出现一个重复的键错误(因为新标记与旧标记重叠)。我不希望在从集合中删除PageTag后立即将其删除。我希望在父DataContext调用SubmitChanges()时将其删除。PageTagString属性的更新语义无意与对象的任何其他数据属性不同。如何从我的Page类中访问DataContext?向PageTag分部类添加DataContext成员。分部类PageTag{DataClassesDataContext myDataContext=new DataClassesDataContext();公共字符串TagString{..等。这不是用于检索页面的父DataContext的独立DataContext吗
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
public Page Page
{
    ...
}