0

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>
Benjamin
  • 3,134
  • 6
  • 36
  • 57

1 Answers1

3

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.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • This is so annoying problem when u deal with master detail forms where user can add new entities, delete or update existing entities in detail section of the form. The simplest method unfortunately is to delete detail entities and recreate them. – Muhammad Adeel Zahid Jul 19 '11 at 07:53
  • The more correct is loading the entity graph and merge changes into the graph because it will generate much less data modification SQL statements. – Ladislav Mrnka Jul 19 '11 at 08:37
  • is it possible in L2S or is its a feature of EF. plz refer some tutorials – Muhammad Adeel Zahid Jul 19 '11 at 08:39
  • It is not feature of neither of them. You must do the [merge manually](http://stackoverflow.com/questions/3635071/update-relationships-when-saving-changes-of-ef4-poco-objects/3635326#3635326) (you must code the logic). – Ladislav Mrnka Jul 19 '11 at 08:41
  • @Ladislav It seems to me like I was using both Independent and FK associations.. Is it worth using only IA's and updating relationships through `ObjectContext`? Seems like that would be ideal. Thanks for the links. I am still trying to understand exactly which scenarios do/don't require FK's in EF 4.1 – Benjamin Jul 20 '11 at 00:15
  • From Ladislav's Blog: **"One-to-one relation in EFv4 always uses Foreign Key association and many-to-many relation always uses Independent association. This makes all stuff around associations even fuzzier."** - I'm assuming this is true for EF 4.1 as well as 4.0. I guess I will just put foreign keys almost everywhere and then I can use them if I get stuck anywhere. – Benjamin Jul 20 '11 at 18:34