EF Code first TPH, doesnt update discriminator column when type is changed

601 views Asked by At

I am learning the Entity framework and struggling with TPH while deleting records. I have created the POCO as follows

 public class Transaction
    {
        public int Id { get; set; }
        public DateTime TransactionDate { get; set; }
        public string Item { get; set; }
        public double Amount { get; set; }
        public virtual PaymentDetail PaymentDetail { get; set; }
    }
    public class Income : Transaction
    {
        public string Source { get; set; }
    }
    public class Expense : Transaction
    {
        public bool IsAvoidable { get; set; }
    }
    public class PaymentDetail
    {
        public int Id { get; set; }
        public DateTime PaymentDate { get; set; }
    }

    public class Cash : PaymentDetail
    {
    }    
    public class BankTransfer : PaymentDetail
    {
        public string TransactionNumber { get; set; }
    }    
    public class BankCheque : PaymentDetail
    {
        public string ChequeNumber { get; set; }
    }
    public class Card : PaymentDetail
    {
        public BankAccount BankAccount { get; set; }
    }

and in my DbContext I have overridden OnModelCreating method. I have following code there.

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<User>().ToTable("User");
        modelBuilder.Entity<Transaction>().ToTable("Transaction");
        modelBuilder.Entity<Income>().ToTable("Income");
        modelBuilder.Entity<Expense>().ToTable("Expense");
        modelBuilder.Entity<PaymentDetail>().ToTable("PaymentDetail");
        modelBuilder.Entity<Liability>().ToTable("Liability");

        modelBuilder.Entity<Transaction>().HasOptional(p => p.PaymentDetail);
    }

I have written code to insert and update transaction. Insert works fine. It creates the the transaction with paymentDetail (as Cash). But when I update it and I change PaymentDetail as BankTransfer, it does update the details in PaymentDetail (Which is a flat table created as TPH). but it does not change the discriminator column. It remains as Cash as it was first created with Cash type. I found by googling that Discriminator column does not change. So I tried to delete that row but it now gives Following error

The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Transaction_dbo.PaymentDetail_PaymentDetail_Id". The conflict occurred in database "MyRecordsDB", table "dbo.Transaction", column 'PaymentDetail_Id'. The statement has been terminated.

The TransactionTable created by EF code first has PaymentDetail_ID can has allow null, then can anyone please tell me way is this problem?

Is it good to use TPH in such cases?

For reference below is the method to modify Transaction from my RepositoryClass.

    public Transaction ModifyTransaction(Transaction tran)
    {
        using (_ctx = new Context())
        {       
            tran = _ctx.Transactions.Attach(tran);

            //Check if the Payment detail is modified
            if(tran.PaymentDetail != null)
            {
                PaymentDetail currentPaymentDetail = null;
                using (var ctx = new Context())
                {
                    currentPaymentDetail = ctx.PaymentDetails.Where(p => p.Id == tran.PaymentDetail.Id).FirstOrDefault();    

                    if (currentPaymentDetail.GetType() != tran.PaymentDetail.GetType())
                    {
                        //Remove current payment detail record
                        ctx.PaymentDetails.Remove(currentPaymentDetail);
                        ctx.SaveChanges();
                         _ctx.Entry<PaymentDetail>(tran.PaymentDetail).State = EntityState.Added;
                    }
               }                                          
            }
            else
            {
                //Do not modify the Payment detail
                _ctx.Entry<PaymentDetail>(tran.PaymentDetail).State = EntityState.Unchanged;
            }   


            _ctx.Entry<Transaction>(tran).State = EntityState.Modified;                
            _ctx.Entry<Profile>(tran.profile).State = EntityState.Unchanged;
            _ctx.SaveChanges();
        }
        return tran;
    }
0

There are 0 answers