多对多自我参考表-GUID ID会自行更改

时间:2018-09-05 18:47:33

标签: .net asp.net-core entity-framework-core asp.net-core-2.1 entity-framework-core-2.1

我有很多表引用。它本身听起来很疯狂,所以这里有一张可以更好地查看问题的图像  https://i.stack.imgur.com/lj2zZ.png

我将guid用作主键和外键。 当我尝试向数据库中添加Foo的新实例时,该实例与数据库中的某些foo有关系,并且在通过行myDbContext.Set<Foo>().Add(foo);的确切时刻,向导{{3} }更改为https://i.stack.imgur.com/WlFW9.png

创建数据库的代码:

 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);
        }
    }

实际测试:

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);
        }
    }

知道为什么会这样吗?它与我的

有关系吗

关系还是? PS(该示例使用foos进行了简化:))

1 个答案:

答案 0 :(得分:1)

我认为问题在于映射Foo.Dependents <-> DependencyFoo.DependentFoo.DependentsOf <-> DependencyFoo.DependentOf

foo的实际依存关系应该类似于(伪代码):

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);

请注意如何交换集合在主要实体中的角色和链接实体中的参考导航属性。要获取依赖项,您必须选择链接的依赖项,并按依赖项 of 进行过滤。反之亦然。

当前,您设置了dependencyFoo.DependentOfId = fooDb.Id,还设置了foo.DependentsOf.Add(dependencyFoo);。它们既代表一个又一个,因此,当您调用add时,EF Core修补程序将为for.Id生成新的Guid并将其分配给dependencyFoo.DependentOfId。同时,dependencyFoo.Dependent保留为null,并且由于dependencyFoo.DependentId的值为Guid.Empty,因此EF Core会为其创建并分配新的Guid。

要解决此问题,只需在流利的映射中交换集合即可:

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);

或者(效果是相同的,我发现它更易于阅读和遵循):

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);