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

时间:2022-09-18 08:14:35

I'm having a difficult time figuring out how to update my entities and their related data. Using Lazyloading..

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

I have the following entity models

我有以下实体模型

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

With the corresponding view models. I've stripped them down, removed validations and such.

具有相应的视图模型。我已将它们剥离,删除了验证等。

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

In my Edit.cshtml form I dynamically add new datafields, and when I post the form, they are properly deserialised to ConfigViewModel.DataFields. So long everything is working. But how do I convert these models, and update the entitymodels? If I post new datafields, their id's will be 0, and they should be added, but the ones that already have an Id, should be updated.. I don't know how to do this, and can't find anything related, or that I could understand.

在我的Edit.cshtml表单中,我动态添加新的数据字段,当我发布表单时,它们被正确地反序列化为ConfigViewModel.DataFields。一切都在运作。但是,我如何转换这些模型,并更新实体模型?如果我发布新的数据字段,他们的id将为0,并且应该添加它们,但是已经有Id的那些应该被更新..我不知道怎么做,并且找不到任何相关的,或者我能理解。

I have the following in my ConfigController.

我的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);
    }
}

In my repository I have:

在我的存储库中,我有:

public void Edit(TEntity entity)
{
    var entry = Context.Entry<TEntity>(entity);
    entry.State = EntityState.Modified;
}

My Edit.cshtml form looks like

我的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 个解决方案

#1


0  

It looks like a couple of things are happening here.

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

If I understand your data structures correctly, you have a Config object that has zero or more DataField objects associated with it.

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

The Edit page of your application for editing Config objects allows you to add new DataField items or modify existing DataField items.

用于编辑Config对象的应用程序的“编辑”页面允许您添加新的DataField项或修改现有的DataField项。

I'm assuming that in the commented section of your example:

我假设在你的例子的评论部分:

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

that you're translating the View Models back to your domain objects.

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

Now I'm going to assume that you are using a per-request lifetime for the DbContext, as that's most typical in these scenarios. So, on each web request, a new DbContext is instantiated as part of the instantiation chain of the MVC Controller and its dependencies (e.g. services, repositories, unit of work, etc.). So on this new request for editing Config objects, the DbContext is empty—it has no knowledge of any objects because it's a brand new DbContext.

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

At this point, you need to Attach the Config entity to the DbContext so that the DbContext will start tracking it. If anything changed about the Config entity (e.g. was the name changed, or were new DataField objects added to the collection?), you will need to set the state of the entity within the DbContext to Modified (this you have done in your example above).

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

Attaching the Config entity to the DbContext will also result in attaching all the related DataField objects that are referenced by the edited Config entity. But there's a slight wrinkle. You will need to tell the DbContext which entities are new, and which are modified. You can easily tell new entities from modified entities because the DataField.Id property will be 0. But how do you tell if an existing DataField entity has been returned unchanged? This is important, because simply attaching the DataField entities to the DbContext puts them in an Unmodified state within the context. So, if there were any changes on an existing entity, those changes would not be persisted when committing the context.

将Config实体附加到DbContext还将导致附加由编辑的Config实体引用的所有相关DataField对象。但是有轻微的皱纹。您需要告诉DbContext哪些实体是新的,哪些是被修改的。您可以轻松地从修改后的实体中告诉新实体,因为DataField.Id属性将为0.但是,如何判断现有DataField实体是否未更改?这很重要,因为简单地将DataField实体附加到DbContext会使它们在上下文中处于未修改状态。因此,如果现有实体有任何更改,则在提交上下文时不会保留这些更改。

A naive approach to solve this problem would entail setting all DataField entities whose Id property is non-zero to the Modified state. This will increase load on the database (but for a small application, this will be negligible). However, if you have any triggers or some other mechanism for auditing when records are created or updated, this is not a good solution. Assuming you are not performing auditing, the naive approach may work well:

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

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();
}

Again, this is a naive approach that should generally work well for small applications. At the very least, it should give you a better idea of the kinds of things you need to be aware of when working with Entity Framework in a disconnected N-Tier scenario.

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

Also, I highly recommend you check out the following books:

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

Each of those books discusses the scenario you're talking about. You may want to start with the Code First book since you are using code first.

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

HTH

#1


0  

It looks like a couple of things are happening here.

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

If I understand your data structures correctly, you have a Config object that has zero or more DataField objects associated with it.

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

The Edit page of your application for editing Config objects allows you to add new DataField items or modify existing DataField items.

用于编辑Config对象的应用程序的“编辑”页面允许您添加新的DataField项或修改现有的DataField项。

I'm assuming that in the commented section of your example:

我假设在你的例子的评论部分:

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

that you're translating the View Models back to your domain objects.

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

Now I'm going to assume that you are using a per-request lifetime for the DbContext, as that's most typical in these scenarios. So, on each web request, a new DbContext is instantiated as part of the instantiation chain of the MVC Controller and its dependencies (e.g. services, repositories, unit of work, etc.). So on this new request for editing Config objects, the DbContext is empty—it has no knowledge of any objects because it's a brand new DbContext.

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

At this point, you need to Attach the Config entity to the DbContext so that the DbContext will start tracking it. If anything changed about the Config entity (e.g. was the name changed, or were new DataField objects added to the collection?), you will need to set the state of the entity within the DbContext to Modified (this you have done in your example above).

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

Attaching the Config entity to the DbContext will also result in attaching all the related DataField objects that are referenced by the edited Config entity. But there's a slight wrinkle. You will need to tell the DbContext which entities are new, and which are modified. You can easily tell new entities from modified entities because the DataField.Id property will be 0. But how do you tell if an existing DataField entity has been returned unchanged? This is important, because simply attaching the DataField entities to the DbContext puts them in an Unmodified state within the context. So, if there were any changes on an existing entity, those changes would not be persisted when committing the context.

将Config实体附加到DbContext还将导致附加由编辑的Config实体引用的所有相关DataField对象。但是有轻微的皱纹。您需要告诉DbContext哪些实体是新的,哪些是被修改的。您可以轻松地从修改后的实体中告诉新实体,因为DataField.Id属性将为0.但是,如何判断现有DataField实体是否未更改?这很重要,因为简单地将DataField实体附加到DbContext会使它们在上下文中处于未修改状态。因此,如果现有实体有任何更改,则在提交上下文时不会保留这些更改。

A naive approach to solve this problem would entail setting all DataField entities whose Id property is non-zero to the Modified state. This will increase load on the database (but for a small application, this will be negligible). However, if you have any triggers or some other mechanism for auditing when records are created or updated, this is not a good solution. Assuming you are not performing auditing, the naive approach may work well:

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

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();
}

Again, this is a naive approach that should generally work well for small applications. At the very least, it should give you a better idea of the kinds of things you need to be aware of when working with Entity Framework in a disconnected N-Tier scenario.

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

Also, I highly recommend you check out the following books:

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

Each of those books discusses the scenario you're talking about. You may want to start with the Code First book since you are using code first.

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

HTH