I'm having a difficult time figuring out how to update my entities and their related data. Using 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.
I have the following in my 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
@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>
}
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.
The Edit page of your application for editing Config
objects allows you to add new DataField
items or modify existing DataField
items.
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 (eg 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
.
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 (eg 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).
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.
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:
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.
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.
HTH
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.