Entity Framework: How to avoid Discriminator column from table?

40,217

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

Solution 2

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

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

Solution 4

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

Solution 5

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

Share:
40,217
LCJ
Author by

LCJ

.Net / C#/ SQL Server Developer Some of my posts listed below -- http://stackoverflow.com/questions/3618380/log4net-does-not-write-the-log-file/14682889#14682889 http://stackoverflow.com/questions/11549943/datetime-field-overflow-with-ibm-data-server-client-v9-7fp5/14215249#14215249 http://stackoverflow.com/questions/12420314/one-wcf-service-two-clients-one-client-does-not-work/12425653#12425653 http://stackoverflow.com/questions/18014392/select-sql-server-database-size/25452709#25452709 http://stackoverflow.com/questions/22589245/difference-between-mvc-5-project-and-web-api-project/25036611#25036611 http://stackoverflow.com/questions/4511346/wsdl-whats-the-difference-between-binding-and-porttype/15408410#15408410 http://stackoverflow.com/questions/7530725/unrecognized-attribute-targetframework-note-that-attribute-names-are-case-sen/18351068#18351068 http://stackoverflow.com/questions/9470013/do-not-use-abstract-base-class-in-design-but-in-modeling-analysis http://stackoverflow.com/questions/11578374/entity-framework-4-0-how-to-see-sql-statements-for-savechanges-method http://stackoverflow.com/questions/14486733/how-to-check-whether-postback-caused-by-a-dynamic-link-button

Updated on December 06, 2020

Comments

  • LCJ
    LCJ over 3 years

    I have the following table created using Entity Framework Code First approach.

    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 PaymentID instead of Payment_ PaymentID? Are there any attributes to achieve this?

    Note: Runtime version for EntityFramework.dll is 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();
        }
    }
    
  • Sander Kouwenhoven
    Sander Kouwenhoven almost 9 years
    Be aware that TPT is not recommended for EF when performance is an issue. See: msdn.microsoft.com/en-US/data/hh949853 under 7.1.1
  • nikib3ro
    nikib3ro about 8 years
    Also, adding [NotMapped] as class attribute helps when you don't want EF to map your subclasses to database tables
  • Vitani
    Vitani about 8 years
    Thank-you, exactly the answer I was looking for!
  • Rostov
    Rostov over 7 years
    If you prefer to use Fluent API for this instead of attribute/annotations you can edit your DbContext and in the OnModelCreating method add: modelBuilder.Ignore<TypeToIgnore>();
  • TvdH
    TvdH over 6 years
    @kape123: that solved my problem, I derived from an entity but only to add some collections not to be stored in the db.
  • Richard Marskell - Drackir
    Richard Marskell - Drackir almost 6 years
    This worked for me. By adding [NotMapped] to the base class, it ignored the class but used the properties for it in the mapped child class.