gpt4 book ai didi

c# - 尝试通过属性默认值更改关系时发生意外的InvalidOperationException

转载 作者:行者123 更新时间:2023-12-03 15:46:23 25 4
gpt4 key购买 nike

在下面的示例代码中,当执行db.Entry(a).Collection(x => x.S).IsModified = true时,出现以下异常:

System.InvalidOperationException: 'The instance of entity type 'B' cannot be tracked because another instance with the key value '{Id: 0}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.



为什么不添加而不是附加B的实例?

奇怪的是, IsModified的文档没有将 InvalidOperationException指定为可能的异常(exception)。无效的文档或错误?

我知道这段代码很奇怪,但是我写它只是为了了解ef core在某些奇怪的egde情况下是如何工作的。我想要的是一个解释,而不是变通的方法。
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
public class A
{
public int Id { get; set; }
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
}

public class B
{
public int Id { get; set; }
}

public class Db : DbContext {
private const string connectionString = @"Server=(localdb)\mssqllocaldb;Database=Apa;Trusted_Connection=True";

protected override void OnConfiguring(DbContextOptionsBuilder o)
{
o.UseSqlServer(connectionString);
o.EnableSensitiveDataLogging();
}

protected override void OnModelCreating(ModelBuilder m)
{
m.Entity<A>();
m.Entity<B>();
}
}

static void Main(string[] args)
{
using (var db = new Db()) {
db.Database.EnsureDeleted();
db.Database.EnsureCreated();

db.Add(new A { });
db.SaveChanges();
}

using (var db = new Db()) {
var a = db.Set<A>().Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
db.SaveChanges();
}
}
}

最佳答案

提供的代码中的错误原因如下。

从数据库中获得创建的实体A时,将使用包含两个新记录S的集合来初始化其属性B。每个新的Id实体的B等于0

// This line of code reads entity from the database
// and creates new instance of object A from it.
var a = db.Set<A>().Single();

// When new entity A is created its field S initialized
// by a collection that contains two new instances of entity B.
// Property Id of each of these two B entities is equal to 0.
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };

执行完代码行 var a = db.Set<A>().Single()集合后,实体 SA不包含来自数据库的 B实体,因为 DbContext Db不使用延迟加载,并且没有显式加载集合 S。实体 A仅包含在集合 B初始化期间创建的新 S实体。

当您调用 IsModifed = true进行收集时, S Entity Framework 会尝试将这两个新实体 B添加到变更跟踪中。但这失败了,因为两个新的 B实体都具有相同的 Id = 0:
// This line tries to add to change tracking two new B entities with the same Id = 0.
// As a result it fails.
db.Entry(a).Collection(x => x.S).IsModified = true;

您可以从堆栈跟踪中看到, Entity Framework 试图将 B实体添加到 IdentityMap中:
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(InternalEntityEntry internalEntityEntry, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(Object relatedEntity, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.set_IsModified(Boolean value)

错误消息还表明它无法跟踪带有 BId = 0实体,因为已经跟踪了另一个具有相同 BId实体。

如何解决此问题。

要解决此问题,您应该在初始化 B集合时删除创建 S实体的代码:
public ICollection<B> S { get; set; } = new List<B>();

相反,您应该在创建 S的位置填充 A集合。例如:
db.Add(new A {S = {new B(), new B()}});

如果您不使用延迟加载,则应显式加载 S集合以将其项添加到更改跟踪中:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;

Why doesn't it add instead of attach the instances of B?



简而言之,由于具有 Detached状态,因此它们被附加以被添加。

执行代码行后
var a = db.Set<A>().Single();

创建的实体 B实例的状态为 Detached。可以使用下面的代码进行验证:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);

然后当你设定
db.Entry(a).Collection(x => x.S).IsModified = true;

EF尝试添加 B实体来更改跟踪。从 EFCore的源代码中,您可以看到这将我们带到带有下一个参数值的 InternalEntityEntry.SetPropertyModified方法:
  • property-我们的B实体之一
  • changeState = true
  • isModified = true
  • isConceptualNull = false
  • acceptChanges = true

  • 具有此类参数的此方法将 Detached B实体的状态更改为 Modified,然后尝试开始对其进行跟踪(请参阅 490-506行)。因为 B实体现在具有状态 Modified,这导致它们被附加(未添加)。

    关于c# - 尝试通过属性默认值更改关系时发生意外的InvalidOperationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60415361/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com