Asp.net mvc 4 首先使用ef代码更新复杂类型

Asp.net mvc 4 首先使用ef代码更新复杂类型,asp.net-mvc-4,ef-code-first,entity-framework-5,complextype,Asp.net Mvc 4,Ef Code First,Entity Framework 5,Complextype,我有一个名为account的复杂类型,它包含一个许可证列表。 许可证又包含一个域列表(域是一个简单的id+url字符串) 在我的存储库中,我有以下代码 public void SaveLicense(int accountId, License item) { Account account = GetById(accountId); if (account == null) { return;

我有一个名为account的复杂类型,它包含一个许可证列表。 许可证又包含一个域列表(域是一个简单的id+url字符串)

在我的存储库中,我有以下代码

    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,任何已删除的域都不在列表中

所以我想要的是

  • 列表和数据库中未更改的任何域-不会发生任何变化
  • 列表和数据库中的任何域,但在列表中已更改-数据库将更新
  • 应将id为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();
     }
    

    不幸的是,更新对象图(具有其他相关实体的实体)是一项相当困难的任务,并且实体框架并没有提供非常复杂的支持来简化

    问题在于,将实体的状态设置为
    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();
    //...
    
    您还可以尝试以通用方式对任意分离的对象图执行这项工作


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

    谢谢,正如我所担心的那样,但至少我现在已经确认了,并且没有因为不得不这样做而感到如此不安。。。如果是这样的话:-)但是如果他们能够在EF@RickardLiljeberg:是的,您可以使用上面的代码,但这只是一个子集合。如果你有多个收藏品和孙子孙女,那就不好笑了。。。顺便说一句:您可以投票支持以下功能:。但这不会发生在EF 7之前(如果有的话),所以可能至少要等一年。我很高兴地投了票,事实上我很惊讶地发现EF在这方面是多么有限。这听起来像个笑话。这个答案确实让我在第一步就质疑了我对EntityFramework和nhibernate的选择——我们还有一段合理的发布时间,我正在考虑转换。