使用Entity Framework(Code First),我正在尝试映射以下两个实体之间的条件/过滤关系:
建筑
BuildingId
BuildingName
区域
areaID表示
的ParentId
AREANAME
IsSubArea
建筑物可以有多个区域
区域可以包含多个(子)区域
我想创建构建和区域之间的关系,其中标记为'IsSubArea'的区域已从关系中过滤掉。在此上下文中,ParentId将与 Building 相关联,否则,ParentId将是另一个 Area 。这将允许我创建一个具有许多区域的建筑物,并且每个区域可以具有许多子区域,从而创建树形结构。
与我发现的解决方案最接近的是“软删除”功能(source):
modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));
转换为适合我的示例:
modelBuilder.Entity<Area>().Map(m => m.Requires("IsSubArea").HasValue(false));
但据我所知,这与建筑物的关系无关。
另一种解决方案是在建筑上创建一个属性,该属性指定用于返回相关区域的查询定义(source):
public class Building
{
public int BuildingId {get; set;}
public string BuildingName {get; set;}
public IQueryable<Area> BuildingAreas
{
get
{
return from area in areas
where area.IsSubArea == false
and area.ParentId == BuildingId
select area;
//Assume I have a reference to relevant DbSets
}
}
}
这个解决方案可行,但不像条件映射那样优雅。
另一个解决方案是继承 Area 并创建2个子类:
BuildingArea
areaID表示
BuildingId
AREANAME
分区
areaID表示
ParentAreaId
AREANAME
每个都继承自 Area ,并根据需要设置“IsSubArea”字段。 这个解决方案感觉更整洁,但我不知道如何在Entity Framework中实现它。
有没有办法在关系上指定条件映射?
有没有更好的方法来实现这种结构?
更新1 :找到this&amp; this继承指南似乎符合我的要求。但是,这些教程都没有定义派生类型之间的关系。我将在今晚用关于每层结构表(TPH)方法尝试的内容更新问题。
更新2 :
我将尝试描述我尝试基于上面的教程链接实现的每层次表(TPH)方法。请原谅我,如果这有点复杂(也许我在想它)。
模型
建筑类与OP保持一致。
我创建了一个抽象基类,用于定义每个派生类型(BuildingArea和SubArea)共有的Area属性:
public abstract class Area
{
protected Area(bool isSubArea)
{
IsSubArea = isSubArea;
SubAreas = new List<SubArea>();
}
public int AreaId { get; set; }
public int ParentId { get; set; }
public string AreaName { get; set; }
public bool IsSubArea { get; private set; } //note the private set
public virtual ICollection<SubArea> SubAreas { get; set; }
}
然后我有2个派生类型,它们继承自 Area :
public class BuildingArea : Area
{
public BuildingArea () : base(false)
{}
public virtual Building ParentBuilding { get; set; }
}
public class SubArea : Area
{
public SubArea(): base(true)
{}
// This is of type `Area` because parent could be either `BuildingArea` or `SubArea`
public virtual Area Parent { get; set; }
}
然后我有以下2个EntityTypeConfigurations:
public class BuildingAreaMap : EntityTypeConfiguration<BuildingArea>
{
public BuildingAreaMap ()
{
// Primary Key
HasKey(t => t.AreaId);
// Properties
Property(t => t.AreaName)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
ToTable("Areas");
Property(t => t.AreaId).HasColumnName("AreaId");
Property(t => t.ParentId).HasColumnName("ParentId");
Property(t => t.AreaName).HasColumnName("AreaName");
Property(t => t.IsSubArea).HasColumnName("IsSubArea");
// This is the discriminator column
Map(m => m.Requires("IsSubArea").HasValue(false));
HasRequired(a => a.Site).WithMany(s => s.SiteAreas).HasForeignKey(k => k.ParentId);
}
public class SubAreaMap : EntityTypeConfiguration<SubArea>
{
public SubAreaMap()
{
// Primary Key
HasKey(t => t.AreaId);
// Properties
Property(t => t.AreaName)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
ToTable("AssetHealthAreas");
Property(t => t.AreaId).HasColumnName("AreaId");
Property(t => t.ParentId).HasColumnName("ParentId");
Property(t => t.AreaName).HasColumnName("AreaName");
Property(t => t.IsSubArea).HasColumnName("IsSubArea");
// This is the discriminator column
Map(m => m.Requires("IsSubArea").HasValue(true));
HasRequired(a => a.Parent).WithMany(s => s.SubAreas).HasForeignKey(k => k.ParentId);
}
}
此代码构建成功,但我收到以下运行时错误:
Map was called more than once for type 'SiteArea' and at least one of the calls didn't specify the target table name.
但我正在指定目标表名(每个EntityTypeConfiguration类中一次)。
所以我删除了SubArea
的EntityTypeConfiguration,但我得到了同样的错误。
其中一个教程将映射拉出EntityTypeConfiguration类,并将其放在OnModelCreating
处理程序中,如下所示:
modelBuilder.Entity<Area>()
.Map<BuildingArea>(m => m.Requires("IsSubArea").HasValue(false))
.Map<SubArea>(m => m.Requires("IsSubArea").HasValue(true));
这也给了我同样的错误。
如果我从等式中删除关系,我会得到关于ParentId
属性的不同错误:
The foreign key component 'ParentId' is not a declared property on type 'SiteArea'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
更新3
我想要创建的模型的图像......
更新4
我将尝试简化我的模型以匹配以下内容。 如果下面的解决方案有效,我将需要更多的业务逻辑来导航树,但它应该是可管理的。
答案 0 :(得分:0)
创建TPH类时的错误: 我认为这是因为您不应该将鉴别器列作为类中的属性。
从基类中删除属性IsSubArea。
当您创建新实例时,EF应自动检测类型并相应地填充IsSubArea。