2

我有一个称为帐户的复杂类型,其中包含许可证列表。 许可证又包含一个域列表(域是一个简单的id + url字符串)。使用ef代码优先更新复杂类型

在我的仓库我有这样的代码

public void SaveLicense(int accountId, License item) 
    { 

     Account account = GetById(accountId); 
     if (account == null) 
     { 
      return; 
     } 


     if (item.Id == 0) 
     { 
      account.Licenses.Add(item); 
     } 
     else 
     { 
      ActiveContext.Entry(item).State = EntityState.Modified; 
     } 


     ActiveContext.SaveChanges(); 
    } 

当我尝试保存更新的许可证(含修改域)发生的事情是属于直许可证字符串得到更新就好了。

但是没有域名更新。


我应该提到,我所做的是允许用户在用户界面中添加和删除域。任何新的域获得id = 0,任何删除的域都不在列表中。

所以我要的是

  1. 是在列表中,数据库和任何未更改域 - 什么也没有发生
  2. 是在列表和数据库中的任何领域,但在列表中改变 - 数据库被更新
  3. 与ID的任何结构域= 0应插入(添加)到数据库
  4. 任何域NOT在列表中但在数据库应被删除

我已经玩了一下没有成功,但我有一个鬼鬼祟祟的怀疑,我在大图中做错了事,所以如果我误解某些设计方面的东西或者只是错过了某些东西,我会喜欢提示。

回答

3

不幸的是更新的对象图 - 与其他相关实体的实体 - 是一个相当艰巨的任务,并没有从实体框架没有非常成熟的支持,使之容易。

问题是,将实体的状态设置为Modified(或者通常为其他任何状态)只会影响您传入DbContext.Entry的实体及其标量属性。它对导航属性和相关实体没有影响。

您必须通过加载当前存储在数据库中的实体(包括相关实体)并将UI中已完成的所有更改合并到该原始图中来手动处理此对象图更新。然后你else情况可能是这样的:

//... 
else 
{ 
    var licenseInDb = ActiveContext.Licenses.Include(l => l.Domains) 
     .SingleOrDefault(l => l.Id == item.Id) 

    if (licenseInDb != null) 
    { 
     // Update the license (only its scalar properties) 
     ActiveContext.Entry(licenseInDb).CurrentValus.SetValues(item); 

     // Delete domains from DB that have been deleted in UI 
     foreach (var domainInDb in licenseInDb.Domains.ToList()) 
      if (!item.Domains.Any(d => d.Id == domainInDb.Id)) 
       ActiveContext.Domains.Remove(domainInDb); 

     foreach (var domain in item.Domains) 
     { 
      var domainInDb = licenseInDb.Domains 
       .SingleOrDefault(d => d.Id == domain.Id); 
      if (domainInDb != null) 
       // Update existing domains 
       ActiveContext.Entry(domainInDb).CurrentValus.SetValues(domain); 
      else 
       // Insert new domains 
       licenseInDb.Domains.Add(domain); 
     } 
    } 
} 
ActiveContext.SaveChanges(); 
//... 

您也可以尝试this project called "GraphDiff"它打算做这项工作在一个通用的方式进行分离的任意对象图。

另一种方法是跟踪UI层中某些自定义字段中的所有更改,然后在数据返回以设置适当的实体状态时评估跟踪的状态更改。因为您处于Web应用程序中,所以它基本上意味着您必须在用户更改值,添加新项目或删除项目时跟踪浏览器中的更改(很可能需要一些Javascript)。在我看来,这个解决方案更难实现。

+0

谢谢你,这是我担心,但至少我现在已确认,并不真的感到非常不满意这样做...如果多数民众赞成如何:-)然而,如果他们能够对EF –

+3

@RickardLiljeberg实施一些很好的支持将会非常好:是的,您可以使用上面的代码,但它只是一个子集合。如果你有多个集合,孙子和孙子......等等,那就不好笑了。顺便说一句:你可以投票选择这个特性:http://entityframework.codeplex.com/workitem/864。但在EF 7之前不会发生(如果有的话),所以可能至少要等待一年。 – Slauma

+0

我投票愉快,我真的很惊讶地发现EF在这方面有多么有限。 –

0

这应该足以做你想要做的事情。如果您对代码有更多疑问,请告诉我。

public void SaveLicense(License item) 
{   
    if (account == null) 
    { 
     context.Licenses.Add(item); 
    } 

    else if (item.Id > 0) 
      { 

       var currentItem = context.Licenses       
        .Single(t => t.Id == item.Id); 

       context.Entry(currentItem).CurrentValues.SetValues(item);      
      } 

    ActiveContext.SaveChanges(); 
} 
+0

这并没有工作 –