实体框架:多个实体具有相同的主键值

时间:2019-07-03 10:26:39

标签: c# entity-framework asp.net-web-api

我正在尝试使用Entity Framework代码优先将下面的对象保存到数据库中,数据库已正确创建,但是当调用SaveChanges();时,它会出现在异常之下。

  

保存或接受更改失败,因为多个“ ConditionalOptions”类型的实体具有相同的主键值。确保显式设置的主键值是唯一的。确保在数据库和实体框架模型中正确配置了数据库生成的主键。将实体设计器用于数据库优先/模型优先配置。使用“ HasDatabaseGeneratedOption”流利的API或“ DatabaseGeneratedAttribute”进行代码优先配置。

有人可以带我去正确的方向吗?

我有这个JSON:

{
    "components": [
        {
            "label": "Text Field",
            "key": "1",
            "conditional": {
                "show": "",
                "when": "",
                "json": ""
            }
        },
        {
            "label": "Columns",
            "columns": [
                {
                "ParentComponentKey":"4",
                    "components": [
                        {
                            "label": "Text Field",
                            "key": "5",
                            "conditional": {
                                "show": "",
                                "when": "",
                                "json": ""
                            }
                        }
                    ],
                    "Id":"1"
                }
            ],
            "key": "4",
            "conditional": {
                "show": "",
                "when": "",
                "json": ""
            }
        },
        {
            "type": "button",
            "label": "Submit",
            "key": "7",
            "disableOnInvalid": true,
            "theme": "primary",
            "input": true,
            "tableView": true
        }
    ]
}

我创建了这些模型类以将数据映射到对象中

public class Form
{
    public Form()
    {
        this.Components = new HashSet<ComponentOptions>();
    }

    [Key]
    public int Id { get; set; }
    public string title { get; set; }
    public virtual ICollection<ComponentOptions> Components { get; set;}
}

public class ComponentOptions
{
    [Key, Column(Order = 0)]
    public int FormId { get; set; }

    [Key, Column(Order = 1)]
    public int Key { get; set; }

    public string label { get; set; }

    public virtual ConditionalOptions conditional { get; set; }
    public virtual ICollection<ColumnOptions> columns { get; set; }

    [ForeignKey("FormId")]
    public virtual Form Form { get; set; }
}

public class ConditionalOptions
{
    [Key, ForeignKey("ComponentOptions"), Column(Order = 0)]
    public Int64 FormId { get; set; }

    [Key, ForeignKey("ComponentOptions"), Column(Order = 1)]
    public int Key { get; set; }
    public string show { get; set; }
    public string when { get; set; }
    public string eq { get; set; }

    public virtual ComponentOptions ComponentOptions { get; set; }
}

public class ColumnOptions
{
    //[ForeignKey("FormId,Key")]
    //public virtual ComponentOptions ComponentOption { get; set; }
    public ColumnOptions()
    {
        this.components = new HashSet<ComponentOptions>();
    }

    [Key, Column(Order = 0)]
    public Int64 FormId { get; set; }

    [Key, Column(Order = 1)]
    public int ParentComponentKey { get; set; } 

    [Key, Column(Order = 2)]
    public int Id { get; set; }

    public string Key { get; set; }
    public int width { get; set; }
    public int offset { get; set; }
    public int push { get; set; }
    public int pull { get; set; }

    public virtual ICollection<ComponentOptions> components { get; set; }

    [ForeignKey("FormId")]
    public virtual Form Form { get; set; }
}

public class EntitesContext : DbContext
{
    public EntitesContext() : base("name=FBEntities")
    {
        Database.SetInitializer<EntitesContext>(new System.Data.Entity.CreateDatabaseIfNotExists<EntitesContext>());
    }

    public IDbSet<Form> FormDataSet { get; set; }
    public IDbSet<ColumnOptions> ColumnOptionsDataSet { get; set; }
    public IDbSet<ComponentOptions> ComponentOptionsDataSet { get; set; }
    public IDbSet<ConditionalOptions> ConditionalOptionsDataSet { get; set;}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

        modelBuilder.Entity<ComponentOptions>()
                  .HasMany(s => s.columns)
                  .WithMany(c => c.components)
                  .Map(cs =>
                  {
                      cs.MapLeftKey("ComponentFormId", "ComponentKey");
                      cs.MapRightKey("ColumnFormId", "ColumnComponentKey", "ColumnId");
                      cs.ToTable("ComponentColumn");
                  });
    }
}

1 个答案:

答案 0 :(得分:0)

问题在ForeignKey模型类中的ConditionalOptions配置中。您已经将相同的导航属性指向了两个外键,这两个外键也是您的复合主键。

由于ComponentOptions的主键是复合键,因此您的ConditionalOptions配置应在以下方面有些棘手,以保持它们之间的one-to-one关系。

public class ConditionalOptions
{
    [Key]
    public Int64 ConditionalOptionsId { get; set; }

    public string show { get; set; }
    public string when { get; set; }
    public string eq { get; set; }

    public virtual ComponentOptions ComponentOptions { get; set; }
}

然后使用Fluent API,如下所示:

modelBuilder.Entity<ConditionalOptions>()
            .HasRequired(co => co.ComponentOptions)
            .WithOptional(c => c.conditional)
            .Map(co => co.MapKey(new []{ "FormId", "Key" }));