如何在不违反主键约束的情况下插入具有循环引用的实体框架

Posted

技术标签:

【中文标题】如何在不违反主键约束的情况下插入具有循环引用的实体框架【英文标题】:How to insert EntityFramework with circular references withouth Violation of PRIMARY KEY constraint 【发布时间】:2017-06-13 13:44:31 【问题描述】:

错误:违反主键约束“PK_dbo.CompanyDtoes”。 无法在对象“dbo.CompanyDtoes”中插入重复键。这 重复键值为 (b20a140d-440b-4a41-b2c3-6763fa752246)。这 语句已终止。

PersonDto

public class PersonDto : PartnerDto, IPartner

    public string FirstName  get; set; 
    public string LastName  get; set; 
    public DateTime BirthDate  get; set; 
    public string BirthPlace  get; set; 
    public string MothersName  get; set; 
    public string TaxId  get; set; 
    public List<CompanyDto> OwnedCompanies  get; set; 

    /// <summary>
    /// Partner címe(i)
    /// </summary>
    public List<PersonAddressDto> Addresses  get; set; 

    public PersonDto()
    
        OwnedCompanies = new List<CompanyDto>();
        Addresses = new List<PersonAddressDto>();
    

CompanyDto

public class CompanyDto : PartnerDto, IPartner

    public string TaxNumber  get; set; 
    public int CompanyValue  get; set; 
    public List<PersonDto> Owners  get; set; 

    /// <summary>
    /// Partner címe(i)
    /// </summary>
    public List<CompanyAddressDto> Addresses  get; set; 

    public CompanyDto()
    
        Owners = new List<PersonDto>();
        Addresses = new List<CompanyAddressDto>();
    

我的 DBContext:

public class PartnerDBContext : DbContext

    public DbSet<PersonDto> Persons  get; set; 

    public DbSet<CompanyDto> Companies  get; set; 

    public DbSet<AddressDto> Addresses  get; set; 

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        modelBuilder.Entity<PersonDto>()
            .HasKey(k => k.PartnerId);

        modelBuilder.Entity<CompanyDto>()
            .HasKey(k => k.PartnerId);

        modelBuilder.Entity<AddressDto>()
            .HasKey(k => k.ID);
    

我尝试插入一个新人,其中包含一些已存在公司的参考:

public bool InsertPerson(PersonDto personToInsert)

    try
    
        using (var db = new PartnerDBContext())
        
            db.Persons.Add(personToInsert);
            db.SaveChanges();
        
    
    catch (Exception ex)
    
        return false;
    

    return true;

我的问题是,我无法插入它,因为它写出违反 CompanyDto 的主键。我知道它已经存在并且我不想添加新的,但我应该如何添加它?我在从 UWP 调用的 WCF 服务中使用它。不幸的是不能使用来自 UWP 的 DataAnnonations(这是一个错误),所以我使用 ModelBuilder...

【问题讨论】:

【参考方案1】:
    public bool InsertPerson(PersonDto personToInsert)
    
        try
        
            using (var db = new PartnerDBContext())
            
                var companies = personToInsert.OwnedCompanies;

                personToInsert.OwnedCompanies = new List<CompanyDto>();

                foreach (var company in companies)
                
                    var companyInDb = db.Companies.Find(company.PartnerId);
                    personToInsert.OwnedCompanies.Add(companyInDb);
                

                db.Persons.Add(personToInsert);
                db.SaveChanges();
            
        
        catch (Exception ex)
        
            return false;
        

        return true;
    

我找到了解决方案,如果我从数据库中获取公司,它可以保存我想要的所有内容。

【讨论】:

【参考方案2】:

问题在于,当您将人员实体添加到上下文时,它还会添加相关实体并将它们标记为Added(即新的)。 SaveChanges() 反过来尝试将它们插入数据库中,您会得到重复的 PK 异常。

您发布的解决方案有效,但涉及检索相关对象的不必要的数据库旅行。由于您知道它们是存在的,因此您可以通过简单地提前将它们附加到上下文来避免这种情况,这会将它们标记为Unchanged(即存在)。然后SaveChanges 将只插入人员记录和链接。

using (var db = new PartnerDBContext())

    foreach (var company in personToInsert.OwnedCompanies)
        db.Companies.Attach(company);
    db.Persons.Add(personToInsert);
    db.SaveChanges();

或者,您可以在将人员添加到上下文后将其标记为 Unchanged

using (var db = new PartnerDBContext())

    db.Persons.Add(personToInsert);
    foreach (var company in personToInsert.OwnedCompanies)
        db.Entry(company).State = EntityState.Unchanged;
    db.SaveChanges();

在我的测试中(最新的 EF6.1.3,短命的新 DbContext 与发布的示例一样)两种方法都有效。

【讨论】:

我之前试过这个,但是当我尝试附加时它也给我写了同样的错误。 :o ... 我在发布之前已经对其进行了测试,并且它可以在我的环境中运行(如果重要,可以使用最新的 EF6.1.3)。 第一个解决方案写道:附加类型为“andocdemo.Model.Dto.Partner.CompanyDto”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用 'Add' 方法或 'Added' 实体状态来跟踪图形,然后将非新实体的状态设置为 'Unchanged' o... 第二个写给我:违反主键约束“PK_dbo.CompanyDtoes”。无法在对象“dbo.CompanyDtoes”中插入重复键。重复键值为 (70fb08e5-8c4a-4dad-8cce-d8a378226921)。声明已终止。 我相信你已经测试过了,但我的解决方案可能是什么问题?

以上是关于如何在不违反主键约束的情况下插入具有循环引用的实体框架的主要内容,如果未能解决你的问题,请参考以下文章

如何在不将实例模型添加到数据库的情况下向实体模型添加FK约束?

尝试插入具有 1:N 关系的实体时,重复键值违反 EntityFramework 中的唯一约束“PK_Users”错误

违反主键约束查询

如何在不明确指定主键的情况下使用 Dapper Extensions 将对象插入 PostGreSql?

mysql中添加外键问题,求高手

如何使用hibernate避免遇到违反唯一约束的问题?