主键冲突:使用EF Code First继承

时间:2012-07-24 14:23:11

标签: c# .net entity-framework ef-code-first

我有以下EF代码的第一个代码。我得到以下例外:

  

'GiftCouponPayment'不包含标识列。

表已在数据库中成功创建。但是,我怎样才能摆脱这种异常呢?另外,这个例外的原因是什么?

注意:我可以使用任何表模式,只要保留域模型(首先使用代码描述)(并且可以查询数据)。

enter image description here

继续此异常后,还有另一个例外情况如下:

  

保存不公开其关系的外键属性的实体时发生错误。 EntityEntries属性将返回null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅InnerException。

     

{“违反PRIMARY KEY约束'PK_dbo.PaymentComponent'。无法在对象'dbo.PaymentComponent'中插入重复键。\ r \ n语句已终止。”}

参考

  1. Entity Framework: Split table into multiple tables
  2. 注意:生成的数据库架构如下所示。

    enter image description here

    代码:

    public class MyInitializer : CreateDatabaseIfNotExists<NerdDinners>
    {
        //Only one identity column can be created per table.
        protected override void Seed(NerdDinners context)
        {
            //context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Payment_PayedTime ON Payment (PayedTime)");
            context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('Payment', RESEED, 1)");
            context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('GiftCouponPayment', RESEED, 2)");
            context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('ClubCardPayment', RESEED, 3)");
        }
    }
    
    //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)
        {
            //Fluent API - Plural Removal
            modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
    
            //Fluent API - Table per Concrete Type (TPC)
            modelbuilder.Entity<GiftCouponPayment>()
                .Map(m =>
                {
                    m.MapInheritedProperties();
                    m.ToTable("GiftCouponPayment");
                });
    
            modelbuilder.Entity<ClubCardPayment>()
                .Map(m =>
                {
                    m.MapInheritedProperties();
                    m.ToTable("ClubCardPayment");
                });
        }
    
        public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
        public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
        public DbSet<Payment> Payments { get; set; }
    }
    
    public abstract class PaymentComponent
    {
        public int PaymentComponentID { get; set; }
        public int MyValue { 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; }
    }
    

    客户端:

        static void Main(string[] args)
        {
            Database.SetInitializer<NerdDinners>(new MyInitializer());
            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;
    
                ClubCardPayment clubCardPayment = new ClubCardPayment();
                clubCardPayment.MyValue = 5000;
    
                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();
            }
        }
    

3 个答案:

答案 0 :(得分:3)

您没有为TPC / TPT映射指定ID字段。即使使用继承,您也需要在不运行TPH映射时执行此操作。 (注意,我也不确定MapInheritedProperties()电话......这通常用于TPH ...而不是TPT)

 //Fluent API - Table per Concrete Type (TPC)
 modelbuilder.Entity<GiftCouponPayment>()
      .HasKey(x => x.PaymentComponentID)
      .Map(m =>
      {
          m.MapInheritedProperties();
          m.ToTable("GiftCouponPayment");
      })
      .Property(x => x.PaymentComponentID)
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

这需要在具体类型的每个类映射上。如果是我,我将使用TPH映射,其中GiftCoupon以及其他继承映射,因此您最终使用1个表来使用鉴别器列表示整个对象树。

无论如何......基类中缺少的另一件事是:

public byte[] Version { get; set; }

以及:

的关联映射
Property(x => x.Version).IsConcurrencyToken()

这允许乐观的并发。

希望这有点帮助,如果您需要进一步的帮助或澄清,请告诉我。

答案 1 :(得分:2)

我看到我对TPC的初步建议不正确,因为你也在基类中使用FK - 你看到PaymentComponent表吗?在TPC继承的情况下不应该存在。尝试使用TPT继承(从映射中删除MapInheritedProperties)。这将以相同的正确数据库结束。不要使用种子。 Id将仅由PaymentComponent表中的标识列控制(就像现在一样)。

答案 2 :(得分:2)

在您的PaymentComponent类上使用KeyAttribute

装饰ID
[Key]
public int PaymentComponentID { get; set; }