2012-07-24 76 views
23

我用下面的表格创建了使用实体框架Code First的方法。实体框架:如何避免表中的Discriminator列?

  1. 如何修改C#代码以便在数据库中不创建不想要的Discriminator列?有没有什么属性可以实现这一点?
  2. 如何使外键列名称为“PaymentID”而不是“Payment_ PaymentID”?有没有什么属性可以实现这一点?

注: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(); 


     } 

    } 
+2

如果您重写约定,其他开发人员将难以理解您的模式。学习和使用约定会更有益,例如,在阅读其他开发人员的代码或默认生成的代码时,您不必提问,也无需编写其他代码。 – user3285954 2014-12-29 11:21:00

回答

29

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

外键列的名称可以再次控制用流利的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")); 
} 
1

正如您使用的是子类,Discriminator列需要区分每种类型的子类。

0

由于“GiftCouponPayment”和“ClubCardPayment”都来自“PaymentComponent”,因此EF不会使用单独的表并且需要该列。如果你想要一个不同的行为,你将不得不重写默认的表访问并将这些字段映射到你的类(我认为你不想这样做)不确定是否有一个简单的方法来实现。从实体首先,我知道有一种方法可以创建表格。
对于外键列名称也是如此。 EF使用这种方式为键/外键名称创建名称。如果你想按照自己的喜好来设置表格的格式,那么你必须自己做所有的事情,这就导致了为什么要使用EF的问题。
除了化妆品之外,您还有什么特别的理由要这样做?

4

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

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

每个表类型(TPT)

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

在EF代码首先实现TPT

我们可以简单地通过将表属性上 子类来指定映射表名(表属性是一个新的 数据标注,并已加入到 创建TPT映射。

:System.ComponentModel.DataAnnotations命名空间中CTP5

如果你喜欢流畅的API,那么你可以通过使用 ToTable()方法创建一个映射TPT 0

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

请注意,性能问题时,不推荐TP用于EF。请参阅:https://msdn.microsoft.com/en-US/data/hh949853下7.1.1 – 2015-06-12 12:26:10

7

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

+2

此外,添加[NotMapped]作为类属性有助于当您不希望EF将您的子类映射到数据库表 – kape123 2016-04-03 07:46:26

+0

感谢 - 你正是我要找的答案! – Jocie 2016-04-07 09:28:10

+0

如果你喜欢使用Fluent API而不是属性/注释,你可以编辑你的_DbContext_并在_OnModelCreating_方法中添加:** modelBuilder.Ignore (); ** – Rostov 2016-10-27 14:32:44

0

示例代码删除Discriminator列,并获取列名为PaymentId作为鉴别器,因此解决您的问题。基于Microsofts 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)); 
    } 
} 
0

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