【问题标题】:Primary Key Violation: Inheritance using EF Code First主键冲突:使用 EF Code First 的继承
【发布时间】:2012-07-24 14:23:10
【问题描述】:

我有以下 EF 代码第一个代码。我收到以下异常:

“GiftCouponPayment”不包含身份列。

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

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

继续这个异常后,还有一个异常如下:

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

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

参考

  1. Entity Framework: Split table into multiple tables

注意:生成的数据库架构如下所示。

代码:

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();
        }
    }

【问题讨论】:

  • 你要两张还是三张桌子?
  • 如果你想要三个支付表,那么你不应该使用MapInheritedProperties()
  • MapInheritedProperties 用于 TPC/TPH。你要求TPT。您需要删除 MapInheritedProperties。
  • @flem 我删除了 MapInheritedProperties 和种子设置。现在我得到了例外。 "在多个位置生成跨实体或关联共享的值。检查映射是否没有将 EntityKey 拆分到多个商店生成的列。{"已添加具有相同键的项目。"}"

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


【解决方案1】:

您没有为 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()

这允许乐观并发。

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

【讨论】:

    【解决方案2】:

    我发现我最初关于 TPC 的建议不正确,因为您还在基类中使用了 FK - 您看到 PaymentComponent 表了吗?在 TPC 继承的情况下,它不应该存在。尝试使用 TPT 继承(从映射中删除 MapInheritedProperties)。这将以相同的正确数据库结束。不要使用补种。 Id 将仅由PaymentComponent 表中的标识列控制(目前如此)。

    【讨论】:

    • 谢谢。我删除了 MapInheritedProperties 和种子设置。现在我得到了例外。 "在多个位置生成跨实体或关联共享的值。检查映射是否没有将 EntityKey 拆分到多个商店生成的列。{"已添加具有相同键的项目。"}"
    【解决方案3】:

    在您的 PaymentComponent 类上用 KeyAttribute 装饰 ID

    [Key]
    public int PaymentComponentID { get; set; }
    

    【讨论】:

    • 升级到 4.3.1 有问题吗?如果没有,我会这样做。甚至是 EF5.0 RC(我还没用过那个!)。
    • 有一个叫NuGet Package Explorer的工具可以让你在VS之外下载包。
    • 即使使用 Key 属性也会出现同样的错误。 [Key] 来自系统。组件模型。数据注释;
    猜你喜欢
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    相关资源
    最近更新 更多