Reputation: 82547
Say I have this table structure:
Client
-----------
ClientId int not null (identity)
CurrentDemographicId int null (FK to ClientDemographic)
OtherClientFields varchar(100) null
ClientDemographic
------------------
ClientDemographicId int not null (identity)
ClientId int not null (FK to Client)
OtherClientDemographicFields varchar(100) null
The idea is that Client (in EF) will have a ClientDemographics list and a CurrentDemographic property.
The problem is when I setup the object structure and try to save it, I get this error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
This error makes sense. I have a circular reference in my table setup. It does not know which entity to insert first (because it needs the Id from both tables at the same time).
So, I hacked together a solution that looks like this:
// Save off the unchanged ClientDemograpic
ClientDemographic originalClientDemographic = client.CurrentClientDemographic;
// Merge the contract into the client object
Mapper.Map(contract, client);
// If this is a new client then add as new to the list.
if (client.ClientId == 0)
{
dataAccess.Add(client);
}
// Restore the original ClientDemographic so that EF will not choke
// on the circular reference.
ClientDemographic newClientDemographic = null;
if (client.CurrentClientDemographic != originalClientDemographic)
{
newCurrentClientDemographic = client.CurrentClientDemographic;
client.CurrentClientDemographic = originalClientDemographic;
}
// save our changes to the db.
dataAccess.SaveChanges();
// Restore updates to ClientDemographics and save (if needed)
if (newClientDemographic != null)
{
client.CurrentClientDemographic = newCurrentClientDemographic;
dataAccess.SaveChanges();
}
But changing the reference back to the previous value, saving, then setting it again so I can save again feels like a hack.
Is there a cleaner way to deal with circular references in EF?
Upvotes: 18
Views: 18323
Reputation: 7135
I'd say the answer is: "not really". The only clean way to deal with the circular reference is to look again at the design and remove it.
In this case - approaching it from a Domain Driven Design perspective - I'd say that Client
is the root of your aggregate and ClientDemographic
is a value object; ClientDemographics
are defined by the values of their 'Other ClientDemographic fields'. You can therefore remove ClientId
from ClientDemographic
, and the problem is prevented instead of cured.
That said, if you're settled on this structure then unfortunately I don't think there's a neat way of handling it in EF, no.
Edit: To give Client
multiple ClientDemographics
as well as a CurrentClientDemographic
property, you can go the other way; remove CurrentClientDemographicId
from Client
, and add an IsCurrent
binary field to ClientDemographic
. The EF then gives you a ClientDemographics
collection property, and you can add the following yourself in a new, partial class:
public partial class Client
{
public ClientDemographic CurrentDemogaphic
{
get { return ClientDemographics.First(cd => cd.IsCurrent); }
}
}
Upvotes: 17
Reputation: 12485
The simple way of avoiding this error is to create your primary object first, SaveChanges and then create your dependant object before calling SaveChanges again.
In this case create the Client first, SaveChanges, then create the ClientDemographic object, add it to the collection and set it as the CurrentDemographic and then SaveChanges again.
Upvotes: 3