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

时间:2014-02-14 13:56:05

标签: c# asp.net asp.net-mvc asp.net-mvc-4 entity-framework-5

我很难搞清楚如何更新我的实体及其相关数据。使用Lazyloading ..

我有以下实体模型

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>
}

1 个答案:

答案 0 :(得分:0)

看起来这里发生了一些事情。

如果我正确理解了您的数据结构,那么您有一个Config对象,其中包含零个或多个DataField个对象。

您的应用编辑页面用于修改Config个对象,您可以添加 DataField 修改现有 DataField项。

我假设在您的示例的评论部分中:

// Do Something with the DataFields
// config.DataFields

您正在将视图模型转换回您的域对象。

现在我假设您正在使用DbContext的每请求生命周期,这在这些情况下是最典型的。因此,在每个Web请求上,新的DbContext被实例化为MVC控制器的实例化链及其依赖性(例如,服务,存储库,工作单元等)的一部分。因此,对于编辑Config个对象的新请求,DbContext为空 - 它不知道任何对象,因为它是一个全新的DbContext

此时,您需要Attach Config实体加DbContext,以便DbContext开始跟踪它。如果有关Config实体的任何更改(例如,名称已更改,或者新的DataField对象是否已添加到集合中?),则需要在{{1}内设置实体的状态} DbContext(这是你在上面的例子中所做的)。

Modified实体附加到Config也会导致附加已编辑的DbContext实体引用的所有相关DataField个对象。但是有轻微的皱纹。您需要告诉Config哪些实体是新的,哪些是经过修改的。您可以轻松地从已修改的实体中告知新实体,因为DbContext属性将为0.但是,如何判断现有DataField.Id实体是否未更改?这很重要,因为只需将DataField实体附加到DataField即可将其置于上下文中的DbContext状态。因此,如果现有实体有任何更改,那么在提交上下文时这些更改将不会保留。

解决此问题的简单方法是将Unmodified属性为非{0}的所有DataField实体设置为Id状态。这将增加数据库的负载(但对于小型应用程序,这可以忽略不计)。但是,如果在创建或更新记录时有任何触发器或其他一些审核机制,则这不是一个好的解决方案。假设您没有执行审计,那么天真的方法可能会很好用:

Modified

同样,这是一种天真的方法,通常应该适用于小型应用程序。至少,它应该让您更好地了解在断开连接的N层方案中使用Entity Framework时需要注意的事项。

此外,我强烈建议您查看以下书籍:

每本书都讨论了你所谈论的场景。您可能希望从Code First书开始,因为您首先使用代码。

HTH