Entity Framework circular dependency for last entity

entity-framework entity-framework-core

Question

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
10
10/16/2016 8:23:21 PM

Accepted Answer

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 Tracks are created in one transaction, EF will try to use the generated What.Id to insert the new Tracks 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 Tracks 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.

17
10/16/2016 8:42:51 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow