问题
I have the following table created using Entity Framework Code First approach.
- 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?
- How do I make the foreign key column named
PaymentID
instead ofPayment_ PaymentID
? Are there any attributes to achieve this?
Note: Runtime version for EntityFramework.dll is v4.0.30XXX.

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();
}
}
回答1:
TPH inheritance needs special column which is used to identify the type of entity. By default this column is called Discriminator
and 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).
The name of foreign key column can be again controlled with 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:
Add attribute [NotMapped] if the property not going to mapped to column.
回答3:
Could also use 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:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BankAccount>().ToTable("BankAccounts");
modelBuilder.Entity<CreditCard>().ToTable("CreditCards");
}
回答4:
As you're using subclasses, the Discriminator column is required to distinguish between each type of your subclasses.
回答5:
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?
回答6:
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.
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));
}
}
回答7:
In order to avoid Discriminator column from table you just need to add annotation [NotMapped] over your derived class.
来源:https://stackoverflow.com/questions/11630515/entity-framework-how-to-avoid-discriminator-column-from-table