Print Page | Close Window

EF and Foreign Keys/Entities

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2010
Forum Discription: For .NET 4.0
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=2098
Printed Date: 10-Jun-2026 at 5:03pm


Topic: EF and Foreign Keys/Entities
Posted By: ken.nelson
Subject: EF and Foreign Keys/Entities
Date Posted: 26-Aug-2010 at 9:16am

This isn't really a DevForce issue, more of an Entity Framework one, and a rather confusing one to us.  We started using DevForce/EF over a year and a half ago, and at that time it seemed that the proper/only way of assigning a foreign-key relationship was to set the child's foreign key to the parent's ID, instead of assigning the child's parent entity property to the new parent entity.  It could be that we prematurely jumped to this conclusion, but it was the only way we could get things to work at the time. 

 

That said, we just spent over a day trying to track down an issue that we hadn't seen before.  We've a parent/child relationship as described above, where the child table (SourcingDecision) has a foreign-key relationship to the parent (UnitCycle) via SourcingDecision.UnitCycleID <==> UnitCycle.UniqueID.  SourcingDecision.UnitCycleID is nullable, so the relationship is a 0/1 to many.  The model generated agrees with this relationship, and sets up the entities properly.  We had a situation where we had a single UnitCycle, and two SourcingDecisions with both SourcingDecisions pointing to the same UnitCycle (child A to parent A, child B to parent A).  We wanted to create a second UnitCycle, and assign one of the SourcingDecisions to the new UnitCycle (child A to parent A, child B to parent B).  Normally what we would do in this case would be to set SourcingDecision B's UnitCycleID property to the new UnitCycle.UniqueID (EntityState == Added), however when saving changes, we would receive the exception below.  For whatever reason, it seems the Entity Framework believes that we're attempting to add a second UnitCycle to a single SourcingDecision (child B to parent A and B) which is definitely not allowed.

 

The workaround appears to be just to assign the entities directly.  In other words, instead of:

 

UnitCycle unitCycle = new UnitCycle();

entityManager.AddEntity(unitCycle);

 

sourcingDecision.UnitCycleID = unitCycle.UniqueID;

entityManager.SaveChanges();

 

What works is:

 

UnitCycle unitCycle = new UnitCycle();

entityManager.AddEntity(unitCycle);

 

sourcingDecision.UnitCycle = unitCycle;

entityManager.SaveChanges();

 

 

But... why?  Why does assigning the key and saving result in an exception where assigning the entity and saving does not.

 

 

A first chance exception of type 'IdeaBlade.EntityModel.EntityManagerSaveException' occurred in IdeaBlade.EntityModel.dll

Multiplicity constraint violated. The role 'UnitCycle' of the relationship 'ProModel_AST_DB.FK_SourcingDecision_UnitCycle' has multiplicity 1 or 0..1.

   at IdeaBlade.EntityModel.EntityManager.HandleSaveResultException(SaveWorkState saveWorkState, Boolean isAsyncOp)

   at IdeaBlade.EntityModel.EntityManager.SaveChangesCore(IEnumerable entities, SaveOptions saveOptions)

   at IdeaBlade.EntityModel.EntityManager.SaveChanges(IEnumerable entities, SaveOptions saveOptions)

   at IdeaBlade.EntityModel.EntityManager.SaveChanges()

   at AST.Common.Sourcing.LockSmith.Unlock(IEnumerable`1 entityInfos) in C:\_source\AST\Current\Common\Sourcing\LockSmith.cs:line 90

   at AST.Web.SourcingService.Source(Guid userID, Guid coaID, ConcurrencyEntityInfo requirementInfo, ConcurrencyEntityInfo unitInfo, SourcingDecisionOptions options) in C:\_source\AST\Current\Web\SourcingService.svc.cs:line 62

Multiplicity constraint violated. The role 'UnitCycle' of the relationship 'ProModel_AST_DB.FK_SourcingDecision_UnitCycle' has multiplicity 1 or 0..1.

   at System.Data.Objects.DataClasses.EntityReference`1.AddToLocalCache(IEntityWrapper wrappedEntity, Boolean applyConstraints)

   at System.Data.Objects.EntityEntry.TakeSnapshotOfSingleRelationship(RelatedEnd relatedEnd, NavigationProperty n, Object o)

   at System.Data.Objects.EntityEntry.TakeSnapshotOfRelationships()

   at System.Data.Objects.Internal.EntityWrapperWithoutRelationships`1.TakeSnapshotOfRelationships(EntityEntry entry)

   at System.Data.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName)

   at System.Data.Objects.DataClasses.RelatedEnd.AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.EntityCollection`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.EntityReference`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.WalkObjectGraphToIncludeAllRelatedEntities(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.EntityCollection`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)

   at System.Data.Objects.DataClasses.RelationshipManager.AddRelatedEntitiesToObjectStateManager(Boolean doAttach)

   at System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity)

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.AddObjectStateEntry(Entity entity, EntityMetadata metadata)

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.HandleModified(Entity entity, EntityMetadata metadata)

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.ProcessEntity(Entity entity, EntityMetadata metadata)

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.ProcessSaves(IEnumerable`1 groupsByType)

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.SaveWithinContext()

   at IdeaBlade.EntityModel.Edm.EdmSaveHelper.Save()

The thread '<No Name>' (0x12b4) has exited with code 0 (0x0).




Replies:
Posted By: kimj
Date Posted: 26-Aug-2010 at 12:18pm
You're right that there should be no difference whether the FK property value is assigned directly or the associated navigation property is assigned.  Over the past few years (since the initial release of DevForce 2009) we have had several bugs - all fixed I believe - with assignments in 1 - 0..1 scenarios, so that's likely how you came to be using the FK directly.
 
I don't have an explanation for the issue you see, since I can't reproduce it in 6.0.4 or current bits.  I'm confused about this association, however, since the error message "'ProModel_AST_DB.FK_SourcingDecision_UnitCycle' has multiplicity 1 or 0..1" would indicate this isn't a 0..1 to many relationship and you shouldn't have been able to have multiple child SourceDecisions for a single UnitCycle to start with, so the relationship should be UnitCycle (0..1) - SourceDecision (M).  If the current multiplicity is correct, then it's possible DevForce has misidentified the parent/child roles.  If you see a generated _fk property in either of these classes, or any incorrectly defined navigation properties, then it's likely the roles were misidentified.  We've done some work to fix this in 6.0.5, which will be officially released next week, so you may want to upgrade to see if the odd behavior goes away.
 


Posted By: ken.nelson
Date Posted: 26-Aug-2010 at 1:18pm
Thanks Kim, we'll make sure to try out 6.0.5 when it's released.
 
That's definitely part of why it's confusing.  The muliplicity in the database seem correct, and also seem correct in the generated model.  The UnitCycle entity class has a SourcingDecisions navigation property, with a Multiplicity of * (Many) and a Return Type indicating "Collection of SourcingDecision", and the SourcingDecision entity class has a UnitCycle navigation property with a Multiplicity of 0..1 (Zero or One) and a Return Type indicating "Instance of UnitCycle".  This seems to match up with other navigation properties' multiplicities in our model.
 
 
 
 


Posted By: kimj
Date Posted: 26-Aug-2010 at 2:02pm

Hmmm.  Also check that the properties on the association itself show the correct multiplicity. 

I would also look at the xml in the EDMX directly to see that both the SSDL and CSDL definitions are what you expect.



Print Page | Close Window