I have an MVC project and using Entity Framework Code First and POCO objects for the database. For example:
public class ClassA
{
public int? Id {get; set;}
public string Name { get; set;}
public virtual ClassB B {get; set;}
}
public class ClassB
{
public int? Id {get;set;}
public string Description {get;set;}
}
I have an ActionResult that create or edit a model. The problem is when I call this ActionResult to update the model, and model.B
has been changed, the relation is not saved in the database. When the ActionResult is called to create a new object it works as expected. How do I solve this?
public ActionResult Save(ClassA model)
{
model.B = GetBFromDb(model.B.Id.Value);
if(ModelState.IsValid)
{
if (id.HasValue)
{
context.Entry(model).State = System.Data.EntityState.Modified;
}
else
{
context.ClassAs.Add(model);
}
context.SaveChanges();
// redirect to index etc.
}
return View("EditForm", model);
}
I solved it by changing ClassA
with a foreign key to ClassB
, and setting the value of BId
:
public class ClassA
{
public int? Id {get; set;}
public string Name { get; set;}
public int BId {get;set;} // foreign key to B
public virtual ClassB B {get; set;}
}
Why does this foreign key property does the job instead of the generated foreign ky of EF?
You can't simply call:
context.Entry(model).State = System.Data.EntityState.Modified;
The entity has to be retrieved from the context first so that EF can begin tracking it. Then you'll want to apply any changes to that entity before calling context.SaveChanges()
.
var entity = context.ClassAs.Find(model.Id);
// set properties you want to modify on entity
entity.Name = model.Name;
entity.ClassB = context.ClassBs.Find(model.ClassB.Id);
// other changes to entity as required...
context.SaveChanges();
This way EF is tracking entity
and knows to apply an update against it.
If you attach and then set the state to modified entity framework will send all properties for update. You don't have to take the otter approach here below as that causes a whole separate load to occur. If you do it that way theres no sense in passing in a model, only an id and then you call TryUpdateModel to fill in the properties from the form.
Personally the attach as modified here is a bit cleaner as it doesn't require a complete round trip because of loading data
http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx