30

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

  1. 如何修改 C# 代码,以便不在数据库中创建不需要的鉴别器列?是否有任何属性可以实现这一目标?
  2. 如何使外键列命名PaymentID而不是Payment_ PaymentID?是否有任何属性可以实现这一目标?

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

在此处输入图像描述

代码

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

客户

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();
    }
}
4

7 回答 7

34

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

外键列的名称可以再次使用 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"));
}
于 2012-07-24T12:58:32.820 回答
9

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

于 2016-01-18T17:48:34.287 回答
3

也可以使用每个类型的表 (TPT)。

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

每个类型的表 (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");
}
于 2014-12-29T16:26:30.537 回答
1

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

于 2012-07-24T12:13:55.637 回答
1

删除鉴别器列并将名为 PaymentId 的列改为鉴别器的示例代码,从而解决了您的两个问题。基于 Microsoft 的 Fluent Api 原始文档。

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));
    }
}
于 2017-07-01T20:35:18.683 回答
1

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

于 2018-02-01T21:50:29.700 回答
0

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

于 2012-07-24T12:17:28.167 回答