【问题标题】:Entity Framework: How to avoid Discriminator column from table?实体框架:如何避免表中的鉴别器列?
【发布时间】:2012-07-22 18:00:34
【问题描述】:

我使用 Entity Framework Code First 方法创建了下表。

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

【问题讨论】:

  • 如果您重写约定,其他开发人员将难以理解您的架构。学习和使用约定更有好处,即您在阅读其他开发人员的代码或默认生成的代码时不必提出问题,也不必编写额外的代码。

标签: c# .net entity-framework ef-code-first


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 此外,当您不希望 EF 将子类映射到数据库表时,添加 [NotMapped] 作为类属性会有所帮助
    • 谢谢,正是我想要的答案!
    • 如果您更喜欢为此使用 Fluent API 而不是属性/注释,您可以编辑您的 DbContext 并在 OnModelCreating 方法中添加:modelBuilder.Ignore();
    • @kape123:这解决了我的问题,我从一个实体派生,但只是添加一些不存储在数据库中的集合。
    • 这对我有用。通过将[NotMapped] 添加到基类,它忽略了该类,但在映射的子类中使用了它的属性。
    【解决方案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 属性放在 子类来指定映射的表名(表属性是一个新的 数据注释并已添加到 CTP5 中的 System.ComponentModel.DataAnnotations 命名空间。

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

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

    【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      删除鉴别器列并将名为 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));
          }
      }
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-12-04
            • 1970-01-01
            • 1970-01-01
            • 2013-10-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多