在DbContext EF 4.1中更新导航属性

时间:2011-07-19 06:03:18

标签: asp.net-mvc entity-framework entity-framework-4.1

经过一整天的研究,我有一些目前正在发挥作用的东西。但是,我不确定我是否真的了解导航属性和实体关系会发生什么,所以我担心我的代码将来可能会出现问题。我不得不手动将导航属性设置为“EntityState.Modified”。我的模型最终可能有许多级别的导航对象和集合。有更简单的方法来更新相关实体吗?如果没有更简单的方法,这种方法可以吗?

这是视图模型

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

实体类

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

上下文

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

控制器

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

并查看

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

1 个答案:

答案 0 :(得分:3)

是的,你做得对。使用Web应用程序中的分离实体时,您必须告诉EF每个实体的确切状态。在您的方案中,您将首先致电:

db.Entry(parent).State = EntityState.Modified;

在EFv4.1中,此操作会导致parent附加到上下文。调用SaveChanges时,只能将附加实体持久保存到数据库。实体的状态设置为已修改,因此上下文将在持久化实体时尝试更新数据库中的现有记录。当您调用该语句时,还会发生一件更重要的事情:所有相关实体也会附加,但它们的状态设置为不变。您必须手动设置所有相关实体的正确状态,因为EF不知道哪个是新的,已修改或已删除。这就是为什么你的以下电话也是正确的。

编辑:

请注意,这会更新父级和子级中的值,但不会更新关系本身。如果您将一个孩子与另一个孩子交换,则在使用independent associations时,该过程会复杂得多。