C# 更新复杂模型绑定EF5 ASP.NET MVC4

C# 更新复杂模型绑定EF5 ASP.NET MVC4,c#,asp.net,asp.net-mvc,asp.net-mvc-4,entity-framework-5,C#,Asp.net,Asp.net Mvc,Asp.net Mvc 4,Entity Framework 5,我很难弄清楚如何更新我的实体及其相关数据。使用懒散的加载 我有以下实体模型 public class Config { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Int32 Id { get; set; } [Required] [MaxLength(100)] public String Name { get; set; } public vi

我很难弄清楚如何更新我的实体及其相关数据。使用懒散的加载

我有以下实体模型

public class Config
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Int32 Id { get; set; }

    [Required]
    [MaxLength(100)]
    public String Name { get; set; }

    public virtual IList<DataField> DataFields { get; set; }
}

public class DataField
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Int32 Id { get; set; }

    [ForeignKey("Config")]
    public Int32 ConfigId { get; set; }

    public virtual Config Config { get; set; }

    [Required, MaxLength(1000)]
    public String Name { get; set; }
}
具有相应的视图模型。我已经将它们剥离,删除了验证等等

public class ConfigViewModel
{
    public Int32 Id { get; set; }

    public String Name { get; set; }

    public IList<DataFieldViewModel> DataFields { get; set; }

    public ConfigModel()
    {
        DataFields = new List<DataFieldViewModel>();
    }
}
public class DataFieldViewModel
{
    [Required]
    public Int32 Id { get; set; }

    public String Name { get; set; }
}
在我的Edit.cshtml表单中,我动态添加了新的数据字段,当我发布表单时,它们会正确地反序列化到ConfigViewModel.datafields。只要一切正常。 但是如何转换这些模型并更新实体模型呢? 如果我发布新的数据域,它们的id将为0,应该添加它们,但是已经有id的应该更新。。 我不知道怎么做,也找不到任何相关的,或者我能理解的

我的ConfigController中有以下内容

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ConfigViewModel model)
{
    try
    {       
        if (!ModelState.IsValid)
            return View();

        var config = uow.Repository<Entity.Models.Config>().FindById(model.Id);

        config.Name = model.Name;

        // Do something with the datafields
        // config.DataFields

        uow.Repository<Entity.Models.Config>().Edit(config);
        uow.Save();

        return RedirectToAction("Index");
    }
    catch(Exception ex)
    {
        ModelState.AddModelError("Error", ex.Message);
        return View(model);
    }
}
在我的存储库中,我有:

public void Edit(TEntity entity)
{
    var entry = Context.Entry<TEntity>(entity);
    entry.State = EntityState.Modified;
}
我的Edit.cshtml表单如下所示

@for(var i = 0; i < Model.DataFields.Count; i++)
{
    <tr>                         
        <td>@Html.HiddenFor(m => m.DataFields[i].Id)</td>
        <td>@Html.TextBoxFor(m => m.DataFields[i].Name)</td>
        <td>@Html.EnumDropDownListFor(m => m.DataFields[i].Type)</td>
    </tr>
}

看起来这里发生了几件事

如果我正确理解了您的数据结构,那么您有一个配置对象,该对象具有零个或多个与之关联的数据字段对象

用于编辑配置对象的应用程序的编辑页面允许您添加新的数据字段项或修改现有的数据字段项

我假设在您示例的注释部分:

// Do Something with the DataFields
// config.DataFields
您正在将视图模型转换回域对象

现在,我将假设DbContext使用每个请求的生存期,这在这些场景中是最典型的。因此,在每个web请求上,一个新的DbContext被实例化为MVC控制器及其依赖项(例如服务、存储库、工作单元等)实例化链的一部分。。因此,在这个编辑配置对象的新请求中,DbContext是空的,它不知道任何对象,因为它是一个全新的DbContext

此时,需要将配置实体附加到DbContext,以便DbContext开始跟踪它。如果有关配置实体的任何更改(例如名称是否更改,或是否将新数据字段对象添加到集合?),则需要在DbContext中设置实体的状态,以修改您在上述示例中所做的操作

将配置实体附加到DbContext还将导致附加被编辑的配置实体引用的所有相关数据字段对象。但是有一点皱纹。您需要告诉DbContext哪些实体是新的,哪些是修改的。您可以轻松区分新实体和修改实体,因为DataField.Id属性将为0。但是,如何判断现有数据字段实体是否已原封不动地返回?这一点很重要,因为只要将数据字段实体附加到DbContext,它们就会在上下文中处于未修改的状态。因此,如果现有实体上有任何更改,那么在提交上下文时这些更改将不会持久化

解决此问题的简单方法需要将Id属性为非零的所有数据字段实体设置为修改状态。这将增加数据库的负载,但对于小型应用程序来说,这可以忽略不计。但是,如果在创建或更新记录时有任何触发器或其他审核机制,这不是一个好的解决方案。假设您没有执行审计,那么朴素的方法可能会很好地工作:

public void Edit(TEntity config)
{
    Context.Attach<TEntity>(config);
    Context.Entry<TEntity>(config).State = EntityState.Modified;

    foreach(var df in config.DataFields)
    {
        Context.Entry<DataField>(df).State = EntityState.Modified;
    }

    // I noticed you never saved the changes to the DbContext. Do you need
    // to do this here, or are you doing it with your UOW somewhere else??
    Context.SaveChanges();
}
同样,这是一种幼稚的方法,通常适用于小型应用程序。至少,它应该让您更好地了解在断开连接的N层场景中使用实体框架时需要注意的事项

此外,我强烈建议您阅读以下书籍:

这些书中的每一本都讨论了你正在谈论的场景。由于您使用的是“代码优先”,因此可能需要从“代码优先”一书开始