我可以在不加载整个集合的情况下删除单个子实体吗?

时间:2015-07-25 15:38:50

标签: asp.net-mvc collections linq-to-entities

我有2个课程,如下所示。

他们可以拥有非常大的馆藏 - 一个网站可能有2,000多个网站页面,反之亦然。

  class WebsitePage 
  {
    public int ID {get;set;}
    public string Title {get;set;}
    public List<Website> Websites {get;set;}
  }

  class Website 
  {
    public int ID {get;set;}
    public string Title {get;set;}
    public List<WebsitePage> WebsitePages {get;set;}
  }

我在从网站上删除WebsitePage时遇到问题。特别是从mutliple网站删除WebsitePage时。

例如,我可能有这样的代码:

var pageToRemove = db.WebsitePages.FirstOrDefault();
var websites = db.Websites.Include(i => i.WebsitePages).ToList();
foreach(var website in websites)
{
    website.WebsitePages.Remove(pageToRemove)
}

如果每个网站Include() 2k页,您可以想象加载第二行需要很长时间。

但是如果我在获取网站时没有Include()网站页面,则没有加载子集合供我删除。

我试图只需Include()我需要删除的页面,但当然保存时会给我一个空集合。

有没有推荐或更好的方法来解决这个问题?

我正在使用现有的MVC网站,除非绝对必要,否则我不想create an entity class for the join table

1 个答案:

答案 0 :(得分:5)

不,你不能......通常。

多对多关系(使用隐藏的联结表)只能通过添加/删除嵌套集合中的项目来影响。为此,必须加载集合。

但是有一些选择。

选项1。

通过原始SQL从联结表中删除数据。基本上这看起来像

context.Database.ExecuteSqlCommand(
    "DELETE FROM WebsiteWebsitePage WHERE WebsiteID = x AND WebsitePageID = y"));

(不使用参数)。

选项2。

将联结包含在类模型中,即将联结表映射到类WebsiteWebsitePageWebsiteWebsitePage现在都有

public ICollection<WebsiteWebsitePage> WebsiteWebsitePages { get; set; }

WebsiteWebsitePage将具有WebsiteWebsitePage的引用属性。现在,您可以直接通过类模型操作联结。

我认为这是最好的选择,因为一切都采用标准的方式来处理具有验证和跟踪的实体。此外,很可能迟早你需要一个明确的联结类,因为你想要为它添加更多的数据。

选项3。

一窍门。

我尝试通过从集合中删除存根实体来实现此目的。在您的情况下:创建一个具有有效主键值的WebsitePage对象,并将其从Website.WebsitePages中删除,而不加载该集合。但是EF并没有注意到这一变化,因为它没有跟踪Website.WebsitePages,并且该项目不在集合中。

但这让我意识到我必须让EF跟踪一个Website.WebsitePages集合,其中包含1个项目,然后删除该项目。我通过首先构建Website项然后将其附加到新上下文来实现此功能。我将展示我使用的代码(标准Product - Category模型)以防止拼写错误。

Product prd;

// Step 1: build an object with 1 item in its collection
Category cat = new Category { Id = 3 }; // Stub entity
using(var db = new ProdCatContext())
{
    db.Configuration.LazyLoadingEnabled = false;
    prd = db.Products.First();
    prd.Categories.Add(cat);
}

// Step 2: attach to a new context and remove the category.
using(var db = new ProdCatContext())
{
    db.Configuration.LazyLoadingEnabled = false;
    db.Products.Attach(prd);

    prd.Categories.Remove(cat);

    db.SaveChanges(); // Deletes the junction record.
}

禁用延迟加载,否则在prd.Categories被解决时仍会加载类别。

我对此处发生的事情的解释是:在第二步中,EF不仅会在您附加产品时开始跟踪产品,还会跟踪产品的关联,因为它知道&#39;你不能在多对多的关系中自己加载这些关联。但是,当您在第一步中添加类别时,它不会这样做。