Reputation: 177163
I have the following problem in a project with EF 4.2. I made a small test project with EF 5.0 (on .NET 4.0) to check if the problem is the same in the newer version - and it is.
Sample model:
public class Order
{
public int TenantId { get; set; }
public int OrderId { get; set; }
public string Name { get; set; }
public int? CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int TenantId { get; set; }
public int CustomerId { get; set; }
public string Name { get; set; }
}
Context and mapping with Fluent API:
public class MyContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasKey(o => new { o.TenantId, o.OrderId });
modelBuilder.Entity<Customer>()
.HasKey(c => new { c.TenantId, c.CustomerId });
modelBuilder.Entity<Order>()
.HasOptional(o => o.Customer)
.WithMany()
.HasForeignKey(o => new { o.TenantId, o.CustomerId });
}
}
The important part here is that Order
references (optionally) a Customer
with a composite foreign key (TenantId, CustomerId)
where the first part TenantId
is part of the primary key (TenantId, OrderId)
at the same time.
I create the database and one Order
...
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
var order = new Order { TenantId = 1, OrderId = 500, Name = "Test Order" };
ctx.Orders.Add(order);
ctx.SaveChanges();
}
...and it works. The database schema looks as expected (a foreign key relationship with principal Customer
and principal key (TenantId, CustomerId)
and dependent Order
and foreign key (TenantId, CustomerId)
).
Then I load the Order
, create a new Customer
, assign it to this Order
and save the changes to update the relationship between Customer
and Order
:
using (var ctx = new MyContext())
{
var order = ctx.Orders.Find(1, 500);
var customer = new Customer { TenantId = 1, CustomerId = 1000,
Name = "Test Customer" };
order.Customer = customer;
// order.TenantId is 1 and customer.TenantId is 1
try
{
ctx.SaveChanges();
}
catch (Exception e)
{
throw;
}
}
When calling SaveChanges
I get an InvalidOperationException
exception:
The property 'TenantId' is part of the object's key information and cannot be modified.
Stacktrace is:
at System.Data.Objects.EntityEntry.VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, Int32 ordinal, String memberName)
at System.Data.Objects.EntityEntry.GetAndValidateChangeMemberInfo(String entityMemberName, Object complexObject, String complexObjectMemberName, StateManagerTypeMetadata& typeMetadata, String& changingMemberName, Object& changingObject)
at System.Data.Objects.EntityEntry.EntityMemberChanging(String entityMemberName, Object complexObject, String complexObjectMemberName)
at System.Data.Objects.EntityEntry.EntityMemberChanging(String entityMemberName)
at System.Data.Objects.ObjectStateEntry.System.Data.Objects.DataClasses.IEntityChangeTracker.EntityMemberChanging(String entityMemberName)
at System.Data.Objects.Internal.SnapshotChangeTrackingStrategy.SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, Int32 ordinal, Object target, Object value)
at System.Data.Objects.Internal.EntityWrapper`1.SetCurrentValue(EntityEntry entry, StateManagerMemberMetadata member, Int32 ordinal, Object target, Object value)
at System.Data.Objects.DataClasses.EntityReference.UpdateForeignKeyValues(IEntityWrapper dependentEntity, IEntityWrapper principalEntity, Dictionary`2 changedFKs, Boolean forceChange)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Objects.ObjectStateManager.PerformAdd(IEntityWrapper wrappedOwner, RelatedEnd relatedEnd, IEntityWrapper entityToAdd, Boolean isForeignKeyChange)
at System.Data.Objects.ObjectStateManager.PerformAdd(IList`1 entries)
at System.Data.Objects.ObjectStateManager.DetectChanges()
at System.Data.Objects.ObjectContext.DetectChanges()
at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force)
at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate)
at System.Data.Entity.Internal.InternalContext.GetStateEntries()
at System.Data.Entity.Infrastructure.DbChangeTracker.Entries()
at System.Data.Entity.DbContext.GetValidationErrors()
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
I don't see where I would change the "part of the key" TenantId
. Its value is 1
in Order
and is 1
in Customer
.
How can a modification of this key property happen with the code above and how can I get the update working?
Upvotes: 3
Views: 645
Reputation: 177163
According to @Pawel's suggestion I've reported the problem as possible bug at Codeplex:
http://entityframework.codeplex.com/workitem/955
At the moment for the model in the question setting the foreign key properties directly instead of setting the navigation property works and doesn't throw an exception:
using (var ctx = new MyContext())
{
var order = ctx.Orders.Find(1, 500);
var customer = new Customer { TenantId = 1, CustomerId = 1000,
Name = "Test Customer" };
ctx.Customers.Add(customer);
order.CustomerId = customer.CustomerId;
try
{
ctx.SaveChanges();
// No exception here
}
catch (Exception e)
{
throw;
}
}
Upvotes: 1
Reputation: 31620
I briefly look at it and seems like a bug where EF "thinks" the TenantId on the Order changes while in fact it does not (i.e. values are the same). Do you mind filing a bug for this on http://entityframework.codeplex.com/?
Upvotes: 1