none
Codefirst TPH mapping issue RRS feed

  • Question

  • Hi,

    I have the following TPH POCO mapping

    public class BillingDetail
    
    
    
        {
    
    
    
            public int Number { getset; }
    
    
    
            public string Owner { getset; }
    
    
    
            public string BillingType { getset; }
    
    
    
            public int BillingId { getset; }
    
    
    
        }
     public class BankAccount:BillingDetail
    
    
    
        {
    
    
    
           public string BankName { getset; }
    
    
    
           public string AccountType { getset; }
    
    
    
           
    
    
    
          
    
    
    
        }
     public class CreditCard:BillingDetail
    
    
    
        {
    
    
    
            public string CardType { getset; }
    
    
    
            public int ExpiryMonth { getset; }
    
    
    
            public int ExpiryYear { getset; }
    
    
    
          
    
    
    
        }

    By DBcontext mapping is as below

    public class TPHContext:DbContext
    
    
    
        {
    
    
    
            public DbSet<BillingDetail> BillingDetails{get;set;}
    
    
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
    
    
            {
    
    
    
                modelBuilder.Entity<BillingDetail>()
    
    
    
                    .Map<BankAccount>(m => { m.Requires("BillingType").HasValue("BB"); })
    
    
    
                    .Map<CreditCard>(m => { m.Requires("BillingType").HasValue("CC"); })
    
    
    
                    .HasKey(m => m.BillingId);
    
    
    
                    //.Property(m => m.BillingType).HasColumnType("nvarchar(max)");
    
    
    
     
    
    
    
                modelBuilder.Entity<BillingDetail>().ToTable("TPHBillingDetails");
    
    
    
                   
    
    
    
                    
    
    
    
                  
    
    
    
     
    
    
    
                    
    
    
    
                   
    
    
    
                base.OnModelCreating(modelBuilder);
    
    
    
            }
    
    
    
        }

    My discriminator column name is BillingType in the TPHBillingDetails table and is of data type varchar(50).

    The code for creating the objects is

     using (TPHContext entity = new TPHContext())
                {
                    CreditCard NewCard = new CreditCard();
                    NewCard.CardType = "Visa";
                    NewCard.ExpiryMonth = 11;
                    NewCard.ExpiryYear = 2011;
                    NewCard.Number = 111;
                    NewCard.Owner ="abc";
                    NewCard.BillingType = "CC";
                    entity.BillingDetails.Add(NewCard);
                    entity.SaveChanges();
                    BankAccount NewBankAccount = new BankAccount();
                    NewBankAccount.AccountType = "Savings";
                    NewBankAccount.BankName = "SBI";
                    NewBankAccount.Number = 112;
                    NewBankAccount.Owner ="bef";
                    NewBankAccount.BillingType = "BB";
                    entity.BillingDetails.Add(NewBankAccount);
                    entity.SaveChanges();
                    MessageBox.Show("Job done");
     
                }

    when I executed the above code I get an error Values of incompatible types  were assigned to the 'BillingType' discriminator column. Values of the same type must be specified. To explicitly specify the type of the discriminator column use the HasColumnType method.

    I tried using the HasColumnType stuff also but it is still failing. Can any one tell what could be the issue.


    Venkatesh. S|MCTS(WCF, ADO.NET 3.5)|eMail: heman_1978@hotmail.com

    Monday, July 11, 2011 2:58 PM

Answers

  • Hello,

    remove BillingType property from your BillingDetail entity. Discriminator column is internal EF property and it is not mapped to the entity - it is converted to .NET type in inheritance hierarchy. Also if your BillingDetail is not abstract it should also have value for discriminator mapped.

    Best regards,
    Ladislav

    Tuesday, July 12, 2011 11:48 AM
  • Hi Alen and Morteza,

    Thanks for looking to the Query and providing me the code sample. I would like to have my understanding corrected. Does that mean that if I just create a new ttype called CreditCard and add that to my context.BankAccount.Add(<<New instance of creditcard>>) and when I do save changes, the DbContext itself is going to set the BillingType to CC and when it is going to be the case of BankAccount to set to BB. Is my understanding correct?

    Thanks and regards

     


    Venkatesh. S|MCTS(WCF, ADO.NET 3.5)|eMail: heman_1978@hotmail.com
    Wednesday, July 13, 2011 1:06 AM
  • Hi,

    right!

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, July 13, 2011 6:43 AM
    Moderator

All replies

  • Hello,

    remove BillingType property from your BillingDetail entity. Discriminator column is internal EF property and it is not mapped to the entity - it is converted to .NET type in inheritance hierarchy. Also if your BillingDetail is not abstract it should also have value for discriminator mapped.

    Best regards,
    Ladislav

    Tuesday, July 12, 2011 11:48 AM
  • Hi Venkatesh,

    Welcome!

    Thanks @Ladislav, you're right!

    You can refer this link: http://blogs.msdn.com/b/adonet/archive/2010/12/10/code-first-mapping-changes-in-ctp5.aspx

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, July 12, 2011 12:24 PM
    Moderator
  • Hi,

    If I remove the Billing type then how would I set the value as CC or BB in the objects that I create. Unless it is a property in base class I will not be able to set the values right? Could you please send me some samples if possible.

    Thanks and regards


    Venkatesh. S|MCTS(WCF, ADO.NET 3.5)|eMail: heman_1978@hotmail.com
    Tuesday, July 12, 2011 1:01 PM
  • Hi,

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations;
    
    namespace ConsoleApplication42
    {
      class Program
      {
        static void Main(string[] args)
        {
          using (var context= new TPHContext() )
          {
            // context.Database.CreateIfNotExists();
            CreditCard NewCard = new CreditCard();
            NewCard.CardType = "Visa";
            NewCard.ExpiryMonth = 11;
            NewCard.ExpiryYear = 2011;
            NewCard.Number = 111;
            NewCard.Owner = "abc";
    
            context.BillingDetails.Add(NewCard);
            context.SaveChanges();
            BankAccount NewBankAccount = new BankAccount();
            NewBankAccount.AccountType = "Savings";
            NewBankAccount.BankName = "SBI";
            NewBankAccount.Number = 112;
            NewBankAccount.Owner = "bef";
            
            context.BillingDetails.Add(NewBankAccount);
            context.SaveChanges();
    
          }
        }
      }
      public class BillingDetail
      {
        public int Number { get; set; }
        public string Owner { get; set; }
        [Key]
        public int BillingId { get; set; }
      }
      public class BankAccount : BillingDetail
      {
        public string BankName { get; set; }
        public string AccountType { get; set; }
      }
      public class CreditCard : BillingDetail
      {
        public string CardType { get; set; }
        public int ExpiryMonth { get; set; }
        public int ExpiryYear { get; set; }
      }
      public class TPHContext : DbContext
      {
        public DbSet<BillingDetail> BillingDetails { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Entity<BillingDetail>()
            .Map<BankAccount>(m => { m.Requires("BillingType").HasValue("BB"); })
            .Map<CreditCard>(m => { m.Requires("BillingType").HasValue("CC"); })
            .HasKey(m => m.BillingId);
          //.Property(m => m.BillingType).HasColumnType("nvarchar(max)");
          modelBuilder.Entity<BillingDetail>().ToTable("TPHBillingDetails");
          base.OnModelCreating(modelBuilder);
        }
    
      }
    }
    
    


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, July 12, 2011 1:41 PM
    Moderator
  • Venkatesh,

    Like Alan shows in his example, you don't need to (and can't) set the discriminator value explicitly. EF will handle this value internally based on your object type.

    -Morteza

    Tuesday, July 12, 2011 3:42 PM
  • Hi Alen and Morteza,

    Thanks for looking to the Query and providing me the code sample. I would like to have my understanding corrected. Does that mean that if I just create a new ttype called CreditCard and add that to my context.BankAccount.Add(<<New instance of creditcard>>) and when I do save changes, the DbContext itself is going to set the BillingType to CC and when it is going to be the case of BankAccount to set to BB. Is my understanding correct?

    Thanks and regards

     


    Venkatesh. S|MCTS(WCF, ADO.NET 3.5)|eMail: heman_1978@hotmail.com
    Wednesday, July 13, 2011 1:06 AM
  • Hi,

    right!

    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, July 13, 2011 6:43 AM
    Moderator