Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Can't Update Data In Table With Composite Key (Oracle)

We have an Oracle table that has a composite key of three columns. These columns are correctly mapped via the Entity Framework Data Model into C# objects. When we query a record from the database and then update a non-key column, we always receive an error saying we are trying to update the primary key (excerpt from a test is below):

var connection = new DbContextProvider(() => new DatabaseConnection()); 
var repo = new Repository(connection); 
var deltas = repo.Queryable<Deltas>().Where(d =>d.Volume.SubmissionId == 88921).ToList();
var deltaToUpdate = deltas.First(); 
deltaToUpdate.RecordedVolume = 0;
repo.Flush();  -- Does a context.SaveChanges() in background

We always receive the following:

System.InvalidOperationException : The property 'COPY_ID' is part of the object's key information and cannot be modified.

COPY_ID is part of the key but is a StoredGeneratedPettern=Identity and it is not changed in the transaction.

Any help appreciated.

Here is the full stack:

System.InvalidOperationException : The property 'COPY_ID' is part of the object's key information and cannot be modified. at Data.Objects.EntityEntry.VerifyEntityValueIsEditable(StateManagerTypeMetadata typeMetadata, Int32 ordinal, String memberName)

at System.Data.Objects.EntityEntry.GetAndValidateChangeMemberInfo(String entityMemberName, Object complexObject, String complexObjectMemberName, ref StateManagerTypeMetadata typeMetadata, ref String changingMemberName, ref 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.EntityEntry.SetCurrentEntityValue(StateManagerTypeMetadata metadata, Int32 ordinal, Object userObject, Object newValue)

at System.Data.Objects.ObjectStateEntryDbUpdatableDataRecord.SetRecordValue(Int32 ordinal, Object value)

at System.Data.Objects.DbUpdatableDataRecord.SetValue(Int32 ordinal, Object value)

at System.Data.Mapping.Update.Internal.UpdateTranslator.SetServerGenValue(P ropagatorResult context, Object value)

at System.Data.Mapping.Update.Internal.UpdateTranslator.BackPropagateServerGen(List`1 generatedValues)

at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)

at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)

at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) at System.Data.Entity.Internal.InternalContext.SaveChanges() at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() at System.Data.Entity.DbContext.SaveChanges()

UPDATE We have traced the db interactions and it appears the following SQL is being run successfully on the database, when it returns to EF the error is thrown (and the change not committed):

declare 
"COPY_ID" number(10,0); 
"CODS_ID" number(10,0); 
"PERIOD_ID" number(7,0); 

begin 
  update "SCHEMA"."TABLE" 
  set "COLUMN" = :p0 
  where ((("COPY_ID" = :p1) 
  and ("CODS_ID" = :p2)) 
  and ("PERIOD_ID" = :p3)) 
  returning "COPY_ID", "CODS_ID", "PERIOD_ID" into "COPY_ID", "CODS_ID",  "PERIOD_ID"; 

  open :p4 
    for select "COPY_ID" as "COPY_ID", "CODS_ID" as "CODS_ID",           "PERIOD_ID" as "PERIOD_ID" 
  from dual; 
end; 

{ :p0=[Decimal,0,Input]0, :p1=[Int32,0,Input]222222, :p2=[Int32,0,Input]22222, :p3=[Int32,0,Input]222222, :p4=[Object,0,Output]NULL }
like image 494
Chris Avatar asked Aug 03 '15 11:08

Chris


1 Answers

Please include:

  • The entity definition
  • The mapping class / configuration from the context
  • The SQL table definition

BackPropagateServerGen

Looking at the stack trace, the key thing I see is BackPropagateServerGen.

Entity Framework is running your update against the database, but one of your composite key values (presumably COPY_ID) is actually being altered by the UPDATE call. This server-generated value is coming back from the SQL call, and Entity Framework is then complaining that the key value is being changed out from under it.

So, I would guess that your COPY_ID composite key value is defined as a server-generated identifier, but one or both of these is happening:

  • You're mapping to a view or a stored procedure which is interfering with what Entity Framework would expect of a vanilla UPDATE
  • You have one or more triggers on the table, view or stored procedure that is interfering with the result.

If you have any triggers, disable them temporarily to see if the problem then stops.

If you're mapping to a view or a stored procedure, try mapping directly to the table if possible.

Use whatever profiling tools you have to capture what SQL is being executed by the code.

Summary

I think the UPDATE call is actually reaching the database, but the return result is changing the key value(s), causing Entity Framework to fail, and likely rolling back the UPDATE transaction (depending on what version of EF you're using).

like image 167
David Moore Avatar answered Nov 03 '22 03:11

David Moore