实体框架和自引用集合
Posted
技术标签:
【中文标题】实体框架和自引用集合【英文标题】:Entity Framework and self-referencing collections 【发布时间】:2021-08-22 01:36:03 【问题描述】:我正在尝试为位置对象建模。位置可以运送到许多其他位置并从许多位置接收。位置也可以有一个父位置。如何通过实体框架在我的数据库中表示它? 这是我到目前为止所拥有的: 位置类:
public class Location
public int Id get; set;
public string Name get; set;
public string ShortName get; set;
public Location ParentLocation get; set;
public int? ParentLocationId get; set;
public ICollection<Location> ShipToLocations get; set;
public ICollection<Location> ReceiveFromLocations get; set;
我的 DbContext 类中的 OnModelCreating 函数:
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Location>(entity =>
entity.HasKey(x => x.Id);
entity.HasOne(x => x.ParentLocation);
entity.HasMany(x => x.ShipToLocations);
entity.HasMany(x => x.ReceiveFromLocations);
);
我似乎正在通过数据库获取 ParentLocationId 字段,这对我来说很有意义。 但是,ef 也会为数据库生成 LocationId 和 LocationId1 字段,这是没有意义的。按照我的理解,Entity Framework 应该生成某种形式的连接表,因为每个位置都有许多运送到位置的位置和许多从位置接收的位置,所有这些位置都是其他位置。
【问题讨论】:
TLDR;.HasOne(x => x.ParentLocation).WithMany()
& .HasMany(x => x.ShipToLocations).WithMany(x => x.ReceiveFromLocations)
.docs.microsoft.com/en-us/ef/core/modeling/…
【参考方案1】:
由于您尝试使用单个表进行此操作,因此 EF 尝试为目标和源位置创建额外的外键。您实际上无法通过一张桌子实现这一目标。您应该有一个额外的表格,表示该位置可用的目的地。
你应该尝试像这样构建你的类:
public class Location
public int Id get; set;
// other fields here
public int? ParentId get; set;
public Location Parent get; set;
public ICollection<Location> ChildLocations get;
public ICollection<LocationDestintaion> Destinations get; set;
public ICollection<LocationDestintaion> Sources get; set;
public class LocationDestintaion
public int SourceId get; set;
public Location Source get; set;
public int DestinationId get; set;
public Location Destination get; set;
那么你会有这种ModelBuilder
配置:
modelBuilder.Entity<Location>()
.HasOne(x => x.Parent).WithMany(x => x.ChildLocations)
.HasForeignKey(x => x.ParentId).IsRequired(false);
modelBuilder.Entity<Location>()
.HasMany(x => x.Destinations).WithOne(x => x.Source)
.HasForeignKey(x => x.SourceId);
modelBuilder.Entity<Location>()
.HasMany(x => x.Sources).WithOne(x => x.Destination)
.HasForeignKey(x => x.DestinationId);
modelBuilder.Entity<LocationDestintaion>().ToTable("Paths")
.HasKey(x => new x.SourceId, x.DestinationId);
然后您可以像这样查询目的地和来源:
const int locationId = 1;
var availableDestinations = context.Locations.Where(x => x.Id == locationId)
.SelectMany(x => x.Destinations).ToList();
var sourceLocations = context.Locations.Where(x => x.Id == locationId)
.SelectMany(x => x.Sources).ToList();
【讨论】:
以上是关于实体框架和自引用集合的主要内容,如果未能解决你的问题,请参考以下文章
使用 IdentityServer4、Asp.Net Core Identity 和自定义提供程序进行 Blazor WebAssembly 身份验证,无需实体框架