繁体   English   中英

EF-Core 尝试插入已存在的关系数据

[英]EF-Core trying to insert relational data that already exists

我正在尝试使用 EF-Core 将一个 object 添加到我的数据库中,该 object 引用了其中的其他对象。 这是我的两个模型:

    public class Contact : Entity
    {
        #nullable enable
        public string? Name1 { get; set; }
        public string? Name2 { get; set; }
        public Company? Company { get; set; }
        public string? Salutation { get; set; }
        public string? Title { get; set; }
        public DateTime? Birthday { get; set; }
        public string? Address { get; set; }
        public string? Postcode { get; set; }
        public string? City { get; set; }
        public string? Country { get; set; }
        public string? Mail { get; set; }
        public string? MailSecond { get; set; }
        public string? PhoneFixed { get; set; }
        public string? PhoneFixedSecond { get; set; }
        public string? PhoneMobile { get; set; }
        public string? Fax { get; set; }
        public string? Url { get; set; }
        public string? SkypeName { get; set; }
        public bool? IsLead { get; set; }
        public string? Remarks { get; set; }
    }

    public class Company : Entity
    {
        #nullable enable
        public string? Name { get; set; }
        public List<Contact>? Contacts { get; set; }
        public string? Address { get; set; }
        public string? Postcode { get; set; }
        public string? City { get; set; }
        public string? Country { get; set; }
        public string? Url { get; set; }
        public string? Remarks { get; set; }
        public string? UpdatedAt { get; set; }
    }

    public class Entity
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public DateTime? CreatedAt { get; set; }
        public DateTime? LastEdited { get; set; }
        public string? BexioId { get; set; }
    }

这是我的 DbContext:

    public class DatabaseContext : DbContext
    {
        public DatabaseContext(DbContextOptions<DatabaseContext> options)
            : base(options)
        {
        }
        public DbSet<Company> Companies { get; set; }
        public DbSet<Contact> Contacts { get; set; }
        
        protected override void OnModelCreating(ModelBuilder modelbuilder)
        {
            modelbuilder.Entity<Contact>().ToTable("Contacts"):
            modelbuilder.Entity<Company>().ToTable("Companies");
        }
    }

尝试使用以下代码插入数据时,出现以下错误:

        public object Add(string values)
        {
            var newContact = new Models.Contact();
            JsonConvert.PopulateObject(values, newContact);

            if (!TryValidateModel(newContact))
                return BadRequest(ModelState);

            _context.Contacts.Add(newContact);
            _context.SaveChanges();

            return Ok();
        }
Microsoft.EntityFrameworkCore.DbUpdateException
  HResult=0x80131500
  Message=An error occurred while updating the entries. See the inner exception for details.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at microtom.portal.Controllers.ContactController.Add(String values) in C:\Users\tpeduzzi\Documents\microtom\microtom.portal\Controllers\ContactController.cs:line 38
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
SqlException: Cannot insert explicit value for identity column in table 'Companies' when IDENTITY_INSERT is set to OFF.

我不完全知道发生了什么。 我认为问题在于 EF-Core 试图在联系人 object 的公司字段中插入 object,即使数据已经在数据库中并且该公司的 Id 字段已经被数据库填写。 谁能告诉我我做错了什么? 几天来,我一直在尝试解决此问题,但我非常绝望……我正在做的正是在线和亲自告诉我的事情。 提前致谢!

_context.Add之前调用_context.Attach(newContact.Company)

EF 使用跟踪的概念来查看更改并将它们传播到数据库。 在您当前的代码中, newContact.Company不是 null,它作为newContact object 图的一部分添加到当前上下文中。 由于上下文对这Company一无所知,它会将其视为新公司并尝试将其保存到数据库中。 DbContext.Attach默认使用未Unchanged的 state 开始跟踪给定实体和可从给定实体访问的条目,因此它会将其视为现有(且未更改)的实体,并且不会尝试将其保存在数据库中。

您正在反序列化来自 JSON 的实体图。 联系人可能在您的数据库中已经存在的 JSON 中有公司参考。 EF 不知道这家公司是否存在,所以它会将其视为一个全新的实体。

根据例外情况,您的 Company 表为其 PK 设置了身份,但是您的 EF 实体的 PK 未设置为 DatabaseGenerated.Identity。 如果是这样,你仍然会遇到问题,但问题是它会插入一个带有新 PK 的重复记录。

Guru 的上述建议是正确的,但为了在所有情况下都安全,您应该检查 DbContext 本地缓存中的所有引用,如果找到则重新关联它们,如果找不到则附加它们。

JsonConvert.PopulateObject(values, newContact);

if (!TryValidateModel(newContact))
    return BadRequest(ModelState);

var existingCompany = _context.Companies.Local.SingleOrDefault(x => x.CompanyId == newContact.CompanyId);
if (existingCompany != null)
    newContact.Company = existingCompany;
else
    _context.Attach(newContact.Comany);

_context.Contacts.Add(newContact);

这需要应用于被反序列化的实体中包含的所有引用。 (以及这些引用的任何引用)99% 的本地检查不会产生任何结果,但在某些情况下它会产生任何结果,例如当您在调用中遇到更新实体集合时,其中一些实体实体引用相同的引用。 反序列化具有相同 ID 的两个对象将创建对相同数据的两个引用。 附加第一个将成功,但附加第二个将失败,因为上下文现在跟踪第一个。

使用分离和/或反序列化的实体很容易导致看似间歇性或情境异常。 小心走路。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM