首先使用ef代码更新复杂类型

时间:2013-05-25 18:25:39

标签: 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;
        }


        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. 应删除列表中但不在数据库中的任何域
  5. 我玩了一点但没有成功,但我有一种偷偷摸摸的怀疑,我在大局中做错了所以如果我误解了某些设计方面或只是错过了什么,我会喜欢提示。 / p>

2 个答案:

答案 0 :(得分: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),同时用户更改值,添加新项或删除项。在我看来,这个解决方案实施起来更加困难。

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