问题
Please consider the following entities
public class What {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Track> Tracks { get; set; }
public int? LastTrackId { get; set; }]
public Track LastTrack { get; set; }
}
public class Track {
public Track(string what, DateTime dt, TrackThatGeoposition pos) {
What = new What { Name = what, LastTrack = this };
}
public int Id { get; set; }
public int WhatId { get; set; }
public What What { get; set; }
}
I use the following to configure the entities:
builder.HasKey(x => x.Id);
builder.HasMany(x => x.Tracks).
WithOne(y => y.What).HasForeignKey(y => y.WhatId);
builder.Property(x => x.Name).HasMaxLength(100);
builder.HasOne(x => x.LastTrack).
WithMany().HasForeignKey(x => x.LastTrackId);
Has you can see there is a wanted circular reference:
What.LastTrack <-> Track.What
when I try to add a Track
to the context (on SaveChanges
in fact):
Track t = new Track("truc", Datetime.Now, pos);
ctx.Tracks.Add(t);
ctx.SaveChanges();
I get the following error:
Unable to save changes because a circular dependency was detected in the data to be saved: ''What' {'LastTrackId'} -> 'Track' {'Id'}, 'Track' {'WhatId'} -> 'What' {'Id'}'.
I would like to say... yes, I know but...
Is such a configuration doable with EF Core ?
回答1:
This is what I like to call the favored child problem: a parent has multiple children, but one of them is extra special. This causes problems in real life... and in data processing.
In your class model, What
(is that a sensible name, by the way?) has Tracks
as children, but one these, LastTrack
is the special child to which What
keeps a reference.
When both What
and Track
s are created in one transaction, EF will try to use the generated What.Id
to insert the new Track
s with WhatId
. But before it can save What
it needs the generated Id of the last Track
. Since SQL databases can't insert records simultaneously, this circular reference can't be established in one isolated transaction.
You need one transaction to save What
and its Track
s and a subsequent transaction to set What.LastTrackId
.
To do this in one database transcation you can wrap the code in a TransactionScope:
using(var ts = new TransactionScope())
{
// do the stuff
ts.Complete();
}
If an exception occurs, ts.Complete();
won't happen and a rollback will occur when the TransactionScope
is disposed.
来源:https://stackoverflow.com/questions/40073149/entity-framework-circular-dependency-for-last-entity