【问题标题】:Entity Framework Core One-One Self Referencing Relationship fails实体框架核心一对一自引用关系失败
【发布时间】:2017-10-29 04:03:06
【问题描述】:

构建迁移时出现以下错误:

无法确定“Location”类型的导航属性“Location.NorthLocation”表示的关系。要么手动配置关系,要么从模型中忽略此属性。

位置实体:

public class Location
{
    public Guid Id { get; set; }

    public DateTime CreatedWhen { get; set; }
    public string CreatedBy { get; set; }
    public DateTime ModifiedWhen { get; set; }
    public string ModifiedBy { get; set; }

    public Guid? NorthLocationId { get; set; }
    public virtual Location NorthLocation { get; set; }

    public Guid? SouthLocationId { get; set; }
    public virtual Location SouthLocation { get; set; }

    public Guid? EastLocationId { get; set; }
    public virtual Location EastLocation { get; set; }

    public Guid? WestLocationId { get; set; }
    public virtual Location WestLocation { get; set; }

}

类型配置:

public class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options)
    {
    }

    public DbSet<Location> Locations { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<T>().HasKey("Id");
        builder.Entity<T>().Property("Id").ValueGeneratedOnAdd();
        builder.Entity<T>().Property("CreatedWhen").HasDefaultValueSql("GETDATE()").ValueGeneratedOnAdd();
        builder.Entity<T>().Property("ModifiedWhen").IsRequired();
        builder.Entity<T>().Property("CreatedBy").HasMaxLength(50).IsRequired();
        builder.Entity<T>().Property("ModifiedBy").HasMaxLength(50).IsRequired();

        // Locations
        builder.Entity<Location>().HasOne(x => x.NorthLocation).WithOne(x => x.SouthLocation).HasForeignKey(typeof(Location), "NorthLocationId").OnDelete(DeleteBehavior.SetNull);
        builder.Entity<Location>().HasOne(x => x.SouthLocation).WithOne(x => x.NorthLocation).HasForeignKey(typeof(Location), "SouthLocationId").OnDelete(DeleteBehavior.SetNull);
        builder.Entity<Location>().HasOne(x => x.EastLocation).WithOne(x => x.WestLocation).HasForeignKey(typeof(Location), "EastLocationId").OnDelete(DeleteBehavior.SetNull);
        builder.Entity<Location>().HasOne(x => x.WestLocation).WithOne(x => x.EastLocation).HasForeignKey(typeof(Location), "WestLocationId").OnDelete(DeleteBehavior.SetNull);
    }

}

我的目标是拥有一个 Location 实体,它可以自引用它自己的邻居到北/南/东/西。

谁能建议我为什么会收到此错误?

【问题讨论】:

    标签: entity-framework entity-framework-core entity-relationship one-to-one self-referencing-table


    【解决方案1】:

    您的模型配置不正确,因为它映射了每个导航属性两次。例如。 SouthLocation 被映射为NorthLocationId 外键的反向导航和SouthLocationId 的直接导航。

    每个导航属性(即NorthLocationSouthLocationEastLocationWestLocation)只能映射到一个关系(即一个外键)。

    如果我删除关系配置部分的第 2 行和第 4 行,模型似乎运行正常。

    一般来说,在 EF Core 中,我们尝试通过让最后一个配置执行 win 来处理冲突配置,但这有一些限制,并且很难预测执行此代码时会发生什么。当然,对 SQL Server 使用 EF Core 2.0 preview1 时,我遇到了一个不同的异常(SQL Server 的错误,抱怨级联增量中的循环依赖关系),因此我们有可能改进了处理这种情况的方式。

    代码如下:

    
        using System;
        using Microsoft.EntityFrameworkCore;
        using Microsoft.EntityFrameworkCore.Metadata;
    
        namespace ConsoleApp4
        {
            class Program
            {
                static void Main(string[] args)
                {
                    using (var db = new MyContext())
                    {
                        db.Database.EnsureDeleted();
                        db.Database.EnsureCreated();
                    }
                }
            }
    
            public class MyContext : DbContext
            {
                public DbSet<Location> Locations { get; set; }
                protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
                {
                    optionsBuilder.UseSqlServer(@"server=(localdb)\mssqllocaldb;database=hey;ConnectRetryCount=0");
                }
    
                protected override void OnModelCreating(ModelBuilder modelBuilder)
                {
                    modelBuilder.Entity<Location>().HasKey("Id");
                    modelBuilder.Entity<Location>().Property("Id")
                       .ValueGeneratedOnAdd();
                    modelBuilder.Entity<Location>().Property("CreatedWhen")
                       .HasDefaultValueSql("GETDATE()")
                       .ValueGeneratedOnAdd();
                    modelBuilder.Entity<Location>().Property("ModifiedWhen")
                       .IsRequired();
                    modelBuilder.Entity<Location>().Property("CreatedBy")
                       .HasMaxLength(50)
                       .IsRequired();
                    modelBuilder.Entity<Location>().Property("ModifiedBy")
                       .HasMaxLength(50)
                       .IsRequired();
                    modelBuilder.Entity<Location>()
                       .HasOne(x => x.NorthLocation)
                       .WithOne(x => x.SouthLocation)
                       .HasForeignKey(typeof(Location), "NorthLocationId")
                       .OnDelete(DeleteBehavior.Restrict);
                    modelBuilder.Entity<Location>()
                       .HasOne(x => x.EastLocation)
                       .WithOne(x => x.WestLocation)
                       .HasForeignKey(typeof(Location), "EastLocationId")
                       .OnDelete(DeleteBehavior.Restrict);
                }
            }
    
            public class Location
            {
                public Guid Id { get; set; }
                public DateTime CreatedWhen { get; set; }
                public string CreatedBy { get; set; }
                public DateTime ModifiedWhen { get; set; }
                public string ModifiedBy { get; set; }
                public Guid? NorthLocationId { get; set; }
                public virtual Location NorthLocation { get; set; }
                public Guid? SouthLocationId { get; set; }
                public virtual Location SouthLocation { get; set; }
                public Guid? EastLocationId { get; set; }
                public virtual Location EastLocation { get; set; }
                public Guid? WestLocationId { get; set; }
                public virtual Location WestLocation { get; set; }
            }
        }
    

    【讨论】:

    • 如果这两行被排除在外,那如何与对应的外键 Id 属性一起工作? ef 是否足够聪明,能够弄清楚并仍然填充它们?
    • 对于每个南北关系和每个东西关系,您在数据库中只会有一个 FK。选择 FK 的位置(例如北排或南排)是完全任意的。这又是一个关系数据库设计问题,而不是 EF Core 问题。顺便说一句,到目前为止我还没有提到它,但我不确定跟踪南北和东西关系是否是模拟实际地理分布位置的好方法。
    • 我尝试删除这两行,但它仍然对我抛出相同的错误。这是用于战略游戏的空间地图。本质上,我希望有能力向任何方向“旅行”。
    • 使用 1.1.2 删除两个反向关系配置后,我收到以下错误:System.Data.SqlClient.SqlException 发生 HResult=0x80131904 Message=Introducing FOREIGN KEY constraint 'FK_Locations_Locations_EastLocationId' on table 'Location ' 可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。无法创建约束或索引。请参阅以前的错误。这是由于 SQL Server 施加的限制。
    • 在将 OnDelete() 调用更改为使用 DeleteBehavior.Restrict 后,我​​正在使用 Location 类而不做任何更改,但必须修复一些在其余代码中未编译的问题。我将更新我的答案以包含完整列表。如果有任何遗漏,请随时在评论中回复。
    【解决方案2】:

    我可以通过将.HasOne() 添加到主模型来解决同样的问题

        // Locations
        builder.Entity<Location>().HasOne(x => x.NorthLocation);
        builder.Entity<Location>().HasOne(x => x.SouthLocation);
        builder.Entity<Location>().HasOne(x => x.EastLocation);
        builder.Entity<Location>().HasOne(x => x.WestLocation);
    

    【讨论】:

    • 你不是也需要HasForeignKey()来电吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-14
    • 2019-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 2020-12-01
    相关资源
    最近更新 更多