C# 如何在每个层次结构(TPH)映射的表中共享公共列名

C# 如何在每个层次结构(TPH)映射的表中共享公共列名,c#,.net,entity-framework-4,ef-code-first,mapping,C#,.net,Entity Framework 4,Ef Code First,Mapping,我使用的是EntityFramework4CTP5代码优先的方法,我有一个逐层次表(TPH)映射。层次结构中的某些类具有相同的属性 public class BaseType { public int Id { get; set; } } public class A : BaseType { public string Customer { get; set; } public string Order { get; set; } } public class B

我使用的是EntityFramework4CTP5代码优先的方法,我有一个逐层次表(TPH)映射。层次结构中的某些类具有相同的属性

public class BaseType
{
    public int Id { get; set; }
}

public class A : BaseType
{
    public string Customer { get; set; }
    public string Order { get; set; }
}

public class B : BaseType
{
    public string Customer { get; set; }
    public string Article { get; set; }
}

public class C : BaseType
{
    public string Article { get; set; }
    public string Manufacturer { get; set; }
}
默认约定将此映射到以下列:

  • 身份证
  • 第1条
  • 第二条
  • 顾客1
  • 顾客2
  • 制造商
  • 命令
  • 类型
我想让EF4共享公共属性,以实现以下目标:

  • 身份证
  • 文章
  • 顾客
  • 制造商
  • 命令
  • 类型
除了减少列的数量之外,它的优点是能够基于Article(例如)搜索记录,而不必知道哪些类型确切地具有Article属性

我尝试将每个公共属性映射到同一列:

modelBuilder.Entity<B>().Property(n => n.Article).HasColumnName("Article");
modelBuilder.Entity<C>().Property(n => n.Article).HasColumnName("Article");
modelBuilder.Entity().Property(n=>n.Article).HasColumnName(“Article”);
modelBuilder.Entity().Property(n=>n.Article).HasColumnName(“Article”);
但这引发了以下异常:

指定的架构无效。错误:(36,6):错误0019:类型中的每个属性名称必须是唯一的。已定义属性名称“Article”


有人知道如何绕过此验证规则吗?

没有绕过此验证的解决方法。在TPH中,列要么属于所有子类继承的基类,要么专用于子类。您不能指示EF将其映射到您的两个孩子,但不能映射到另一个孩子。尝试这样做(例如,将
[Column(Name=“Customer”)]
放在A.Customer和B.Customer上)将导致此消息出现MetadataException:

指定的架构无效。错误: (10,6):错误0019:类型中的每个属性名称必须是唯一的。已定义属性名称“Customer”


TPH解决方案: 一种解决方案是将
Customer
Article
属性提升到基类:

public class BaseType {
    public int Id { get; set; }
    public string Customer { get; set; }
    public string Article { get; set; }
}

public class A : BaseType {
    public string Order { get; set; }
}

public class B : BaseType { }

public class C : BaseType {
    public string Manufacturer { get; set; }
}
这将生成所需的模式:


TPT解决方案(推荐):

这样,我建议考虑使用每种类型的表(TPT),因为它更适合您的场景:

public class BaseType
{
    public int Id { get; set; }
}

public class A : BaseType
{
    [Column(Name = "Customer")]
    public string Customer { get; set; }
    public string Order { get; set; }
}

public class B : BaseType
{
    [Column(Name = "Customer")]
    public string Customer { get; set; }

    [Column(Name = "Article")]
    public string Article { get; set; }
}

public class C : BaseType
{
    [Column(Name="Article")]
    public string Article { get; set; }
    public string Manufacturer { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<BaseType> BaseTypes { get; set; }        

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseType>().ToTable("BaseType");
        modelBuilder.Entity<A>().ToTable("A");
        modelBuilder.Entity<C>().ToTable("C");
        modelBuilder.Entity<B>().ToTable("B");          
    }
}
公共类基类
{
公共int Id{get;set;}
}
公共类A:基类型
{
[列(Name=“客户”)]
公共字符串Customer{get;set;}
公共字符串顺序{get;set;}
}
公共类B:基类型
{
[列(Name=“客户”)]
公共字符串Customer{get;set;}
[列(Name=“Article”)]
公共字符串项目{get;set;}
}
公共类C:基类型
{
[列(Name=“Article”)]
公共字符串项目{get;set;}
公共字符串制造商{get;set;}
}
公共类MyContext:DbContext
{
公共数据库集基类型{get;set;}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
modelBuilder.Entity().ToTable(“BaseType”);
modelBuilder.Entity().ToTable(“A”);
modelBuilder.Entity().ToTable(“C”);
modelBuilder.Entity().ToTable(“B”);
}
}

对于任何遇到此问题的人,它现在已在EF6中修复:

您的建议可能会起作用,但它会污染类层次结构,并违背OO.Agree的目的。这就是为什么我建议你使用TPT,正如我更新的答案所示。谢谢,我看到你添加了TPT建议。这是一个很好的建议,尽管出于性能原因,我仍然希望有一个表。没问题,我认为这是不可能的:
指定的模式无效。错误:(10,6):错误0019:类型中的每个属性名称必须是唯一的。属性名“Customer”已经定义。
我只希望Microsoft在TPH中使用类名前缀而不是顺序后缀。这将使模式更具可读性!事实证明,这是代码优先代码(仍然存在于EF6中)中的一个错误,它在生成数据库模式时生成了两个同名列。EF运行时和EF设计器实际上支持此场景。欢迎在CodePlex中投票支持这个问题:我上面提到的bug在EF6的最新版本中已经修复。EF6中的解决方案:链接已经过时。