简体   繁体   English

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

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

I am trying to add an object that has references to other objects in it to my database using EF-Core.我正在尝试使用 EF-Core 将一个 object 添加到我的数据库中,该 object 引用了其中的其他对象。 These are my two models:这是我的两个模型:

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

This is my DbContext:这是我的 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");
        }
    }

When trying to insert data using the following code, I get the following error:尝试使用以下代码插入数据时,出现以下错误:

        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.

I don't exactly know what is going on.我不完全知道发生了什么。 I believe that the problem is that EF-Core is trying to insert the object in the Company field of the contact object, even though the data is already in the database and the Id field of that company has already been filled out by the database.我认为问题在于 EF-Core 试图在联系人 object 的公司字段中插入 object,即使数据已经在数据库中并且该公司的 Id 字段已经被数据库填写。 Can anybody tell me what I am doing wrong?谁能告诉我我做错了什么? I've been trying to fix this problem for a few days and I am wuite desperate... I am doing exactly what is being told to me online and in person.几天来,我一直在尝试解决此问题,但我非常绝望……我正在做的正是在线和亲自告诉我的事情。 Thanks in advance!提前致谢!

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

EF uses concept of tracking to see the changes and propagate them to database. EF 使用跟踪的概念来查看更改并将它们传播到数据库。 In you current code newContact.Company is not null and it is added to current context as part of newContact object graph.在您当前的代码中, newContact.Company不是 null,它作为newContact object 图的一部分添加到当前上下文中。 Since context does not know anything about this Company it will consider it as a new one and try to save it into the database.由于上下文对这Company一无所知,它会将其视为新公司并尝试将其保存到数据库中。 DbContext.Attach begins tracking the given entity and entries reachable from the given entity using the Unchanged state by default so it will consider it as existing (and unchanged) one and will not try to save it database. DbContext.Attach默认使用未Unchanged的 state 开始跟踪给定实体和可从给定实体访问的条目,因此它会将其视为现有(且未更改)的实体,并且不会尝试将其保存在数据库中。

You are deserializing an entity graph from JSON.您正在反序列化来自 JSON 的实体图。 Contact will likely have a Company reference in your JSON which already exists in your database.联系人可能在您的数据库中已经存在的 JSON 中有公司参考。 EF doesn't know whether this company exists or not so it will treat it as a brand new entity. EF 不知道这家公司是否存在,所以它会将其视为一个全新的实体。

Based on the exception your Company table has an Identity set for it's PK however your EF Entity's PK is not set as DatabaseGenerated.Identity.根据例外情况,您的 Company 表为其 PK 设置了身份,但是您的 EF 实体的 PK 未设置为 DatabaseGenerated.Identity。 You'd still have a problem if it was, but the issue then would be it would insert essentially a duplicate record with a new PK.如果是这样,你仍然会遇到问题,但问题是它会插入一个带有新 PK 的重复记录。

Guru's recommendation above is correct, though to be safe in all cases you should check the DbContext local cache for all references and re-associate them if found, attaching them if not found. 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);

This needs to be applied to all references included with an entity being deserialized.这需要应用于被反序列化的实体中包含的所有引用。 (and any references of those references) 99% of the time the local check doesn't result in anything, but there will be situations that it does, such as when you can encounter updating a collection of entities within a call where some of those entities refer to the same reference. (以及这些引用的任何引用)99% 的本地检查不会产生任何结果,但在某些情况下它会产生任何结果,例如当您在调用中遇到更新实体集合时,其中一些实体实体引用相同的引用。 Deserializing two objects with the same ID will create two references to the same data.反序列化具有相同 ID 的两个对象将创建对相同数据的两个引用。 Attaching the first one will succeed, but attaching the second will fail due to the context now tracking the first.附加第一个将成功,但附加第二个将失败,因为上下文现在跟踪第一个。

Working with detached and/or deserialized entities can easily lead to seemingly intermittent or situational exceptions.使用分离和/或反序列化的实体很容易导致看似间歇性或情境异常。 Walk carefully.小心走路。 :) :)

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

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