Reputation: 13
I have a many to many table that references. Itself it sounds crazy so here's an image for a better view of the problem https://i.sstatic.net/lj2zZ.png
I use guids as primary and foreign keys.
When I try to add a new instance of Foo
to the database which has a relationship to some foo from the database, and on the exact moment when the line myDbContext.Set<Foo>().Add(foo);
is passed, the Guid https://i.sstatic.net/WlFW9.png gets changed to https://i.sstatic.net/989nb.png
Code to create database:
internal class Foo
{
public Guid Id { get; set; }
public string Title { get; set; }
public virtual List<DependencyFoo> Dependents { get; set; }
public virtual List<DependencyFoo> DependentsOf { get; set; }
}
internal class DependencyFoo
{
public virtual Foo Dependent { get; set; }
public Guid DependentId { get; set; }
public virtual Foo DependentOf { get; set; }
public Guid DependentOfId { get; set; }
}
internal class MyDbContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=.\\SQLEXPRESS;Initial Catalog=fooDb;Trusted_Connection=True;Integrated Security=True");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DependencyFoo>()
.HasKey(x => new { x.DependentOfId, x.DependentId });
modelBuilder.Entity<DependencyFoo>()
.HasOne(x => x.DependentOf)
.WithMany(x => x.DependentsOf)
.HasForeignKey(x => x.DependentOfId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DependencyFoo>()
.HasOne(x => x.Dependent)
.WithMany(x => x.Dependents)
.HasForeignKey(x => x.DependentId);
}
}
Actual testing:
internal class Program
{
private static void Main(string[] args)
{
MyDbContext myDbContext = new MyDbContext();
myDbContext.Set<Foo>().Add(new Foo { Title = "Some cool title" });
myDbContext.SaveChanges();
Foo fooDb = myDbContext.Set<Foo>().FirstOrDefault(x => x.Title == "Some cool title");
DependencyFoo dependencyFoo = new DependencyFoo
{
DependentOfId = fooDb.Id
};
Foo foo = new Foo
{
DependentsOf = new List<DependencyFoo>()
};
foo.DependentsOf.Add(dependencyFoo);
myDbContext.Set<Foo>().Add(foo);
}
}
Any idea why this is happening? Does it have to do something with my
relationship or ? PS (the example is simplified with foos :))
Upvotes: 1
Views: 542
Reputation: 205589
I believe the problem is with mappings Foo.Dependents <-> DependencyFoo.Dependent
and Foo.DependentsOf <-> DependencyFoo.DependentOf
.
The actual dependents/dependents of foo
should really be something like (pseudocode):
IEnumerable<Foo> fooDependents = db.Set<DependencyFoo>()
.Where(d => d.DependentOf.Id == foo.Id)
.Select(d => d.Dependent);
IEnumerable<Foo> fooDependentsOf = db.Set<DependencyFoo>()
.Where(d => d.Dependent.Id == foo.Id)
.Select(d => d.DependentOf);
Note how the roles of collections in the main entity and reference navigation properties in the link entity are exchanged. To get the dependents, you have to select linked dependent filtered by dependent of. And vice versa.
Currently you set dependencyFoo.DependentOfId = fooDb.Id
and also foo.DependentsOf.Add(dependencyFoo);
. Both they represents one and the same, so when you call add, the EF Core fixup generates new Guid for for.Id
and assigns it to dependencyFoo.DependentOfId
. At the same time, dependencyFoo.Dependent
remains null
and since dependencyFoo.DependentId
value is Guid.Empty
, EF Core creates and assigns a new Guid to it.
To fix the issue, simply exchange the collections in the fluent mapping:
modelBuilder.Entity<DependencyFoo>()
.HasOne(x => x.DependentOf)
.WithMany(x => x.Dependents)
.HasForeignKey(x => x.DependentOfId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DependencyFoo>()
.HasOne(x => x.Dependent)
.WithMany(x => x.DependentsOf)
.HasForeignKey(x => x.DependentId);
or alternatively (the effect is the same, I just find it easier to read and follow):
modelBuilder.Entity<Foo>()
.HasMany(x => x.Dependents)
.WithOne(x => x.DependentOf)
.HasForeignKey(x => x.DependentOfId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Foo>()
.HasMany(x => x.DependentsOf)
.WithOne(x => x.Dependent)
.HasForeignKey(x => x.DependentId);
Upvotes: 1