实体框架核心One-One自我引用关系失败

时间:2017-05-28 21:59:58

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

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

无法确定导航属性所代表的关系' 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);
    }

}

我的目标是让一个位置实体自我引用它自己的北/南/东/西的邻居。

有人可以建议我为什么会收到此错误吗?

2 个答案:

答案 0 :(得分:1)

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

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

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

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

以下是代码:

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

答案 1 :(得分:0)

我只需将 .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);