After researching all day and night, I have something that is currently working. However, I am not sure I really understand what's going on with navigation properties and entity relationships, so I'm concerned that my code might cause problems in the future. I had to manually set the navigation properties to "EntityState.Modified". My model may eventually have many levels of navigation objects and collections. Is there an easier way to update the related entities? If there is no easier way, is this approach okay?
Here is the view model
public class ViewModel {
public ViewModel() { }
public ViewModel(Context context) {
this.Options = new SelectList(context.Options, "Id", "Name");
}
public Parent Parent { get; set; }
public SelectList Options { get; set; }
}
entity classes
public class Parent {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ChildOne ChildOne { get; set; }
public virtual ChildTwo ChildTwo { get; set; }
}
public class ChildOne {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Parent Parent { get; set; }
public virtual int OptionId { get; set; }
public virtual Option Option { get; set; }
}
public class ChildTwo {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Parent Parent开发者_如何学编程 { get; set; }
public virtual int OptionId { get; set; }
public virtual Option Option { get; set; }
}
public class Option {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ChildOne> ChildrenOnes { get; set; }
public virtual ICollection<ChildTwo> ChildrenTwos { get; set; }
}
context
public class Context : DbContext {
public DbSet<Parent> Parents { get; set; }
public DbSet<ChildOne> ChildrenOnes { get; set; }
public DbSet<ChildTwo> ChildrenTwos { get; set; }
public DbSet<Option> Options { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Parent>()
.HasOptional(x => x.ChildOne)
.WithOptionalPrincipal(x => x.Parent);
modelBuilder.Entity<Parent>()
.HasOptional(x => x.ChildTwo)
.WithOptionalPrincipal(x => x.Parent);
}
}
controller
private Context db = new Context();
public ActionResult Edit() {
ViewModel viewmodel = new ViewModel(db);
viewmodel.Parent = db.Parents.Find(1);
return View(viewmodel);
}
public void Save(Parent parent) {
if (ModelState.IsValid) {
db.Entry(parent).State = EntityState.Modified;
db.Entry(parent.ChildOne).State = EntityState.Modified;
db.Entry(parent.ChildTwo).State = EntityState.Modified;
db.SaveChanges();
}
}
and view
@model MvcApp7.Models.ViewModel
<div id="Parent">
@Html.HiddenFor(model => model.Parent.Id)
@Html.TextBoxFor(model => model.Parent.Name)
<div id="ChildOne">
@Html.HiddenFor(model => model.Parent.ChildOne.Id)
@Html.TextBoxFor(model => model.Parent.ChildOne.Name)
@Html.DropDownListFor(model => model.Parent.ChildOne.OptionId, Model.Options)
</div>
<div id="ChildTwo">
@Html.HiddenFor(model => model.Parent.ChildTwo.Id)
@Html.TextBoxFor(model => model.Parent.ChildTwo.Name)
@Html.DropDownListFor(model => model.Parent.ChildTwo.OptionId, Model.Options)
</div>
</div>
<input id="SaveButton" type="button" value="save" />
<script type="text/javascript">
$('#SaveButton').click(function () {
var data = $('input, select, textarea').serialize();
$.post('@Url.Action("Save")', data, function () { });
});
</script>
Yes you are doing it right. When working with detached entities like in web application you must tell EF exactly what state each entity has. In your scenario you will first call:
db.Entry(parent).State = EntityState.Modified;
In EFv4.1 this operation causes that parent
is attached to context. Only attached entities can be persisted to database when SaveChanges
is called. The state of the entity is set to modified so context will try to update existing record in the database when persisting the entity. There is one more important thing which happened when you called that statement: All related entities are attached as well but their state is set to unchanged. You must manually set correct state of all related entities because EF doesn't know which one is new, modified or deleted. That is why your following calls are correct as well.
Edit:
Be aware that this updates values in parent and child but it doesn't update relation itself. If you swap one child with another the process is much more complicated when using independent associations.
精彩评论