C# 实体框架:如何避免表中的鉴别器列?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11630515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 18:35:26  来源:igfitidea点击:

Entity Framework: How to avoid Discriminator column from table?

c#.netentity-frameworkef-code-first

提问by LCJ

I have the following table created using Entity Framework Code Firstapproach.

我使用实体框架代码优先方法创建了下表。

  1. How do I modify the C# code so that the unwanted Discriminator column is not created in the database? Are there any attributes to achieve this?
  2. How do I make the foreign key column named PaymentIDinstead of Payment_ PaymentID? Are there any attributes to achieve this?
  1. 如何修改 C# 代码,以便不会在数据库中创建不需要的鉴别器列?是否有任何属性可以实现这一目标?
  2. 如何使外键列命名PaymentID而不是Payment_ PaymentID?是否有任何属性可以实现这一目标?

Note: Runtime version for EntityFramework.dll is v4.0.30XXX.

注意:EntityFramework.dll 的运行时版本是v4.0.30XXX

enter image description here

在此处输入图片说明

CODE

代码

public abstract class PaymentComponent
{
    public int PaymentComponentID { get; set; }
    public int MyValue { get; set; }
    public string MyType { get; set; }
    public abstract int GetEffectiveValue();
}

public partial class GiftCouponPayment : PaymentComponent
{
    public override int GetEffectiveValue()
    {
        if (MyValue < 2000)
        {
            return 0;
        }
        return MyValue;
    }
}

public partial class ClubCardPayment : PaymentComponent
{
    public override int GetEffectiveValue()
    {
        return MyValue;
    }
}

public partial class Payment
{
    public int PaymentID { get; set; }
    public List<PaymentComponent> PaymentComponents { get; set; }
    public DateTime PayedTime { get; set; }
}

//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
    public NerdDinners(string connString) : base(connString)
    { 

    }

    protected override void OnModelCreating(DbModelBuilder modelbuilder)
    {
        modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }

    public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
    public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
    public DbSet<Payment> Payments { get; set; }
}

CLIENT

客户

static void Main(string[] args)
{
    string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";

    using (var db = new NerdDinners(connectionstring))
    {                
        GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
        giftCouponPayment.MyValue = 250;
        giftCouponPayment.MyType = "GiftCouponPayment";

        ClubCardPayment clubCardPayment = new ClubCardPayment();
        clubCardPayment.MyValue = 5000;
        clubCardPayment.MyType = "ClubCardPayment";

        List<PaymentComponent> comps = new List<PaymentComponent>();
        comps.Add(giftCouponPayment);
        comps.Add(clubCardPayment);

        var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
        db.Payments.Add(payment);

        int recordsAffected = db.SaveChanges();
    }
}

采纳答案by Ladislav Mrnka

TPH inheritance needs special column which is used to identify the type of entity. By default this column is called Discriminatorand contains names of derived entities. You can use Fluent-API to define different column name and different values. You can also use your MyType column directly because it is actually a discriminator but in such case you cannot have that column in your entity (column can be mapped only once and if you use it as discriminator it is already considered as mapping).

TPH 继承需要特殊的列,用于标识实体的类型。默认情况下,此列被调用Discriminator并包含派生实体的名称。您可以使用 Fluent-API 来定义不同的列名和不同的值。您也可以直接使用 MyType 列,因为它实际上是一个鉴别器,但在这种情况下,您的实体中不能有该列(列只能映射一次,如果您将它用作鉴别器,它已经被视为映射)。

The name of foreign key column can be again controlled with Fluent-API:

Fluent-API 可以再次控制外键列的名称:

protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
    modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();

    // Example of controlling TPH iheritance:
    modelBuilder.Entity<PaymentComponent>()
            .Map<GiftPaymentComponent>(m => m.Requires("MyType").HasValue("G"))
            .Map<ClubPaymentComponent>(m => m.Requires("MyType").HasValue("C"));

    // Example of controlling Foreign key:
    modelBuilder.Entity<Payment>()
                .HasMany(p => p.PaymentComponents)
                .WithRequired()
                .Map(m => m.MapKey("PaymentId"));
}

回答by mathieu

As you're using subclasses, the Discriminator column is required to distinguish between each type of your subclasses.

当您使用子类时,需要使用 Discriminator 列来区分每种类型的子类。

回答by DerApe

Since both "GiftCouponPayment" and "ClubCardPayment" derives from "PaymentComponent" EF will not use separate tables and will need that column. If you want a different behaviour you would have to override the default table access and map the fields to your classes (which I think you don't want to do) Not sure if there is an easy way to to this. From entity first, I know that there is a way through the template which creates the tables.
The same is for the foreign key column name. EF uses that way of creating the name for key/foreignkey names. If you want to format the table as you like, you have to do it all yourself, which leads to the question why use EF then at all.
Is there a particular reason why you want to do that, other than cosmetics?

由于“GiftCouponPayment”和“ClubCardPayment”都派生自“PaymentComponent”,EF 将不会使用单独的表,并且需要该列。如果您想要不同的行为,您将不得不覆盖默认表访问并将字段映射到您的类(我认为您不想这样做)不确定是否有一种简单的方法可以做到这一点。首先从实体开始,我知道有一种方法可以通过模板创建表格。
外键列名也是如此。EF 使用这种方式为键/外键名称创建名称。如果您想根据自己的喜好格式化表格,则必须自己完成所有工作,这导致了为什么要使用 EF 的问题。
除了化妆品之外,您有什么特别的理由想要这样做吗?

回答by user3285954

Could also use Table per Type (TPT).

也可以使用 Table per Type (TPT)。

http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt

http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt

Table per Type (TPT)

Table per Type is about representing inheritance relationships as relational foreign key associations. Every class/subclass that declares persistent properties—including abstract classes—has its own table. The table for subclasses contains columns only for each noninherited property (each property declared by the subclass itself) along with a primary key that is also a foreign key of the base class table.

Implement TPT in EF Code First

We can create a TPT mapping simply by placing Table attribute on the subclasses to specify the mapped table name (Table attribute is a new data annotation and has been added to System.ComponentModel.DataAnnotations namespace in CTP5.

If you prefer fluent API, then you can create a TPT mapping by using ToTable() method:

每种类型的表 (TPT)

每个类型的表是关于将继承关系表示为关系外键关联。每个声明持久属性的类/子类(包括抽象类)都有自己的表。子类表仅包含每个非继承属性(子类本身声明的每个属性)的列以及一个主键,该主键也是基类表的外键。

在 EF Code First 中实现 TPT

我们可以简单地通过在子类上放置 Table 属性来指定映射表名称来创建 TPT 映射(Table 属性是一个新的数据注释,已添加到 CTP5 中的 System.ComponentModel.DataAnnotations 命名空间。

如果您更喜欢 fluent API,那么您可以使用 ToTable() 方法创建 TPT 映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<BankAccount>().ToTable("BankAccounts");
    modelBuilder.Entity<CreditCard>().ToTable("CreditCards");
}

回答by John Li

Add attribute [NotMapped] if the property not going to mapped to column.

如果属性不会映射到列,则添加属性 [NotMapped]。

回答by Ogglas

Sample code to remove Discriminator column and get column named PaymentId as discriminator instead, therefore solving both your questions. Based on Microsofts Fluent Api original documentation.

删除鉴别器列并获取名为 PaymentId 的列作为鉴别器的示例代码,从而解决您的两个问题。基于微软的 Fluent Api 原始文档。

https://msdn.microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396

https://msdn.microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396

public enum MyEnum
{ 
    Value1, Value2
}

public class MyBaseClass

{ 
    [NotMapped]
    public MyEnum PaymentId { get; protected set; }
}

public class DerivedOne: MyBaseClass
{
    public DerivedOne()
    {
        PaymentId = MyEnum.Value1;
    } 
}

public class DerivedTwo: MyBaseClass
{
    public DerivedTwo()
    {
        PaymentId = MyEnum.Value2;
    }
}

public class MyDbContext : DbContext
{
    DbSet<MyBaseClass> MyBaseClass { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<MyBaseClass>()
            .Map<DerivedOne>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value1))
            .Map<DerivedTwo>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value2));
    }
}

回答by Valeriy

In order to avoid Discriminator column from table you just need to add annotation [NotMapped] over your derived class.

为了避免表中的鉴别器列,您只需要在派生类上添加注释 [NotMapped]。