Entity Framework 核心一对一自引用关系失败
Entity Framework Core One-One Self Referencing Relationship fails
构建迁移时出现以下错误:
无法确定类型 '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 实体,该实体将自己的邻居自引用到 north/south/east/west。
谁能告诉我为什么会出现此错误?
您的模型配置不正确,因为它将每个导航属性映射了两次。例如。 SouthLocation
被映射为 NorthLocationId
外键的反向导航和 SouthLocationId
的直接导航。
每个导航 属性(即 NorthLocation
、SouthLocation
、EastLocation
、WestLocation
)只能映射到一个关系(即到一个外国钥匙)。
如果我删除关系配置部分的第 2 行和第 4 行,模型似乎可以正常运行。
一般来说,在 EF Core 中,我们试图通过让最后的配置执行来处理配置冲突,但这有一些局限性,很难预测执行这段代码时会发生什么。当然,针对 SQL 服务器使用 EF Core 2.0 preview1,我得到了一个不同的异常(一个错误形式 SQL 服务器,抱怨级联删除中的循环依赖),所以我们可能改进了我们的方式处理这种情况。
代码如下:
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; }
}
}
我只需将 .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);
构建迁移时出现以下错误:
无法确定类型 '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 实体,该实体将自己的邻居自引用到 north/south/east/west。
谁能告诉我为什么会出现此错误?
您的模型配置不正确,因为它将每个导航属性映射了两次。例如。 SouthLocation
被映射为 NorthLocationId
外键的反向导航和 SouthLocationId
的直接导航。
每个导航 属性(即 NorthLocation
、SouthLocation
、EastLocation
、WestLocation
)只能映射到一个关系(即到一个外国钥匙)。
如果我删除关系配置部分的第 2 行和第 4 行,模型似乎可以正常运行。
一般来说,在 EF Core 中,我们试图通过让最后的配置执行来处理配置冲突,但这有一些局限性,很难预测执行这段代码时会发生什么。当然,针对 SQL 服务器使用 EF Core 2.0 preview1,我得到了一个不同的异常(一个错误形式 SQL 服务器,抱怨级联删除中的循环依赖),所以我们可能改进了我们的方式处理这种情况。
代码如下:
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; }
}
}
我只需将 .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);