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

时间:2009-05-23 05:23:07

标签: linq linq-to-sql

我的数据库中有两个表由外键连接:Page(PageId,其他数据)和PageTag(PageId,Tag)。我已经使用LINQ为这些表生成类,其中页面作为父级,Tag作为子集合(一对多关系)。有没有办法在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.

这是我的具体情况:
为了更容易地处理标签集合,我在Page对象中添加了一个属性,用于将Tag集合视为字符串:

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类中标记要从数据库中删除的子元素?

7 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

亚伦

显然你必须通过你的PageTag记录循环,为每个记录调用DeleteOnSubmit。 Linq to SQL应该创建一个聚合查询,以便在调用SubmitChanges时立即删除所有记录,因此开销应该是最小的。

替换

PageTags.Clear();

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

答案 5 :(得分:0)

亚伦:

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

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

    public string TagString { 

...等。

答案 6 :(得分: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 });
}