Kit
Kit

Reputation: 21709

Foreign keys to different existing entities are not being saved on update of referencing entity

I have a POCO A entity referencing a B entity, let's say b. I want A to reference a different exising B entity, let's say bb.

These steps:

var b = // get existing b from somewhere out-of-context
var a = new A { B = b }
dbcontext.Set<B>.Attach(a.B);
dbcontext.Set<A>.Add(a);
context.SaveChanges();

generate an insert statement for a as expected with the B_ID foreign key properly set to the primary key ID of b. These subsequent steps:

var bb = // get existing bb from somewhere out-of-context
a.B = bb;
differentdbcontext.Set<B>.Attach(a.B);
differentdbcontext.Set<A>.Attach(a);
differentdbcontext.Entry(a).State = EntityState.Modified;
differentdbcontext.SaveChanges();

result in no change to the persisted data. The update statement does not include the set B_ID = ... as expected.

I'm doing something simple wrong as I've had other scenarios like this working before.

Upvotes: 1

Views: 212

Answers (1)

Slauma
Slauma

Reputation: 177133

Setting the state to Modified only has an effect on scalar properties but not on your navigation properties. I assume that B_ID is not a property in your model but only the foreign key column in the database not being exposed to your model.

In this case you can update the relationship only by leveraging the automatic change detection of Entity Framework. One approach - and I would call this the standard approach - is to load the original A including the original B from the database, set a.B to your new bb and then save the changes:

var bb = // get existing bb from somewhere out-of-context

differentdbcontext.Set<B>().Attach(bb);
differentdbcontext.Set<A>().Include(x => x.B).Single(x => x.Id == a.Id);

a.B = bb;

differentdbcontext.SaveChanges();

If you don't want to load the original from the DB some trick programming is required:

var bb = // get existing bb from somewhere out-of-context

if (  (a.B == null && bb != null) 
   || (a.B != null && bb == null)
   || (a.B != null && bb != null && a.B.Id != bb.Id)) //take care not to attach
                                                      //two objects with same key
{
    if (bb != null)
        differentdbcontext.Set<B>().Attach(bb);
    differentdbcontext.Set<A>().Attach(a);
    a.B = bb; // EF will detect this change
}
else if (a.B == null && bb == null)
{
    // create a dummy a.B
    a.B = new B(); // it doesn't matter which Id
    differentdbcontext.Set<A>().Attach(a);
    a.B = bb; // = null -> EF will detect a change
}

differentdbcontext.SaveChanges();

Or similar. The idea is to change the reference after attaching the objects so that change detection will send an update of the FK column to the database.

Exposing foreign keys as properties into your model will make this situation much easier. Setting the state to Modified would work then because FK properties are scalar.

Upvotes: 1

Related Questions