简体   繁体   English

关系的条件映射

[英]Conditional Mapping on Relationships

Using Entity Framework (Code First), I'm trying to map a conditional/filtered relationship between the following 2 entities: 使用实体框架(代码优先),我试图映射以下两个实体之间的条件/过滤关系:

Building 建造
BuildingId BuildingId
BuildingName 建筑名称

Area 区域
AreaId areaID表示
ParentId 的ParentId
AreaName AREANAME
IsSubArea IsSubArea

A Building can have many Areas 建筑可以有很多区域
An Area can have many (Sub) Areas 区域可以有许多(子) 区域

I would like to create the relationship between Building and Area where the areas marked with 'IsSubArea' are filtered out of the relationship. 我想创建建筑区域之间的关系,其中标有'IsSubArea'的区域被过滤掉。 In this context, ParentId would relate to a Building , otherwise, ParentId would be another Area . 在此上下文中,ParentId将与Building相关 ,否则,ParentId将是另一个Area This would allow me to create a building with many areas, and each area could have many sub-areas, creating a tree style structure. 这将允许我创建具有许多区域的建筑物,并且每个区域可以具有许多子区域,从而创建树形结构。

The closest to a solution I have found relates to 'soft delete' functionality ( source ): 最接近我发现的解决方案与'软删除'功能( )有关:

modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));

Converted to fit my example: 转换为适合我的例子:

modelBuilder.Entity<Area>().Map(m => m.Requires("IsSubArea").HasValue(false));

But as far as I can tell, this has no bearing on the relationship to the Building. 但据我所知,这与建筑物的关系无关。

Another solution would be to create a property on the Building which specifies the query definition to use to return related areas ( source ): 另一种解决方案是在Building上创建一个属性,该属性指定用于返回相关区域的查询定义( ):

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

This solution would work but doesn't feel as elegant as a conditional mapping. 这个解决方案可以工作,但不像条件映射那样优雅。

Another solution would be to inherit from Area and create the 2 sub-classes: 另一种解决方案是从Area继承并创建2个子类:

BuildingArea BuildingArea
AreaId areaID表示
BuildingId BuildingId
AreaName AREANAME

SubArea 分区
AreaId areaID表示
ParentAreaId ParentAreaId
AreaName AREANAME

Each inherits from Area and sets the 'IsSubArea' field as appropriate. 每个都继承自Area并根据需要设置'IsSubArea'字段。 This solution feels tidier but I do not know how to implement this in Entity Framework. 这个解决方案感觉更整洁,但我不知道如何在Entity Framework中实现它。

Is there a way to specify conditional mapping on relationships? 有没有办法在关系上指定条件映射?
Is there a better way to implement this structure? 有没有更好的方法来实现这种结构?

Update 1 :Found this & this guide on inheritance which seems to match my requirements. 更新1 :找到这个这个继承的指南,似乎符合我的要求。 However, neither of these tutorials define relationships between derived types. 但是,这些教程都没有定义派生类型之间的关系。 I'll update the question tonight with what I have tried with regards to the Table per Hierarchy (TPH) method. 我将在今晚用关于每层结构表(TPH)方法尝试的内容更新问题。

Update 2 : 更新2
I'm going to try an describe the Table per Hierarchy (TPH) method I tried to implement based on the tutorial links above. 我将尝试描述我尝试基于上面的教程链接实现的每层次表(TPH)方法。 Forgive me if this gets a little complicated (maybe I'm over thinking it). 请原谅我,如果这有点复杂(也许我在想它)。

Models 楷模
The building class remains the same as the OP. 建筑类与OP保持一致。

I have created an abstract base class defining the Area properties common to each derived type (BuildingArea and SubArea): 我创建了一个抽象基类,定义了每个派生类型(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; }
}

I then have 2 derived types which inherit from Area : 然后我有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; }        
}

I then have the following 2 EntityTypeConfigurations: 然后我有以下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);
    }
}

This code builds successfully, but I do get the following runtime error: 此代码构建成功,但我收到以下运行时错误:

Map was called more than once for type 'SiteArea' and at least one of the calls didn't specify the target table name.

But I am specifying the target table name (once in each EntityTypeConfiguration class). 但我正在指定目标表名称(每个EntityTypeConfiguration类中一次)。 So I removed the EntityTypeConfiguration for SubArea but I get the same error. 所以我删除了SubArea的EntityTypeConfiguration但是我得到了同样的错误。

One of the tutorials pulls the mapping out of the EntityTypeConfiguration class and puts it in the OnModelCreating handler as follows: 其中一个教程将映射拉出EntityTypeConfiguration类,并将其放入OnModelCreating处理程序中,如下所示:

modelBuilder.Entity<Area>()
    .Map<BuildingArea>(m => m.Requires("IsSubArea").HasValue(false))
    .Map<SubArea>(m => m.Requires("IsSubArea").HasValue(true));

This also gives me the same error. 这也给了我同样的错误。

If I remove relationships from the equation, I get a different error regarding the ParentId property: 如果我从等式中删除关系,我会得到关于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.

Update 3 更新3

An image of the model I'm trying to create... 我试图创建的模型的图像......

ERD

Update 4 更新4

I'm going to try and simplify my model to match the following. 我将尝试简化我的模型以匹配以下内容。 If the solution below works, I will need to have a little more business logic to navigate the tree but it should be manageable. 如果下面的解决方案有效,我将需要更多的业务逻辑来导航树,但它应该是可管理的。

ERD2

For the errors when creating the TPH classes: I think this is because you are not supposed to have the discriminator column as a property in your classes. 对于创建TPH类时的错误:我认为这是因为您不应该将鉴别器列作为类中的属性。

Remove the property IsSubArea from your base class. 从基类中删除属性IsSubArea。

When you create new instance, EF should automatically detect the type and fill the IsSubArea accordingly. 创建新实例时,EF应自动检测类型并相应地填充IsSubArea。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM