【发布时间】:2016-12-16 23:44:40
【问题描述】:
当我尝试这样做时遇到以下异常:
context.Entry(testType).State = EntityState.Modified;
System.InvalidOperationException: The instance of entity type 'TestType' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
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.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry.set_State(EntityState value)
我没有看到任何代码已经跟踪了具有相同键的另一个 TestType 实例!
数据库中的测试类型通过.AsNoTracking();加载
那么在这段代码中,TestType 的实例与我执行 RemoveRange/AddRange 操作的键相同吗?那意味着我的 TestTypeComparer 坏了?
我在这里尝试做的是让用户一次性批量保存更改/添加/删除的实体:
public async Task<IEnumerable<TestType>> SaveTestTypesAsync(List<TestType> testTypes, int schoolyearId, int schoolclassId, int subjectId)
{
var testTypesFromDatabase = await context.TestTypes
.Include(t => t.Subject)
.Include(s => s.Schoolclass)
.Where(p =>
p.Schoolclass.Id == schoolclassId &&
p.Subject.Id == subjectId)
.AsNoTracking()
.ToListAsync();
var schoolclass = new Schoolclass { Id = schoolclassId };
var subject = new Subject { Id = subjectId };
var schoolyear = new Schoolyear { Id = schoolyearId };
foreach (var testType in testTypes)
{
testType.Schoolclass = schoolclass;
testType.Subject = subject;
testType.Schoolyear = schoolyear;
}
var testTypesToRemove = testTypesFromDatabase.Except(testTypes, new TestTypeComparer()).ToList();
context.TestTypes.RemoveRange(testTypesToRemove);
var testTypesToAdd = testTypes.Where(t => t.Id == 0).ToList(); //
context.TestTypes.AddRange(testTypesToAdd);
var modifiedTestTypesToUpdate = testTypes.Except(testTypesToAdd.Concat(testTypesToRemove).ToList(), new TestTypeComparer()).ToList();
foreach (var testType in modifiedTestTypesToUpdate)
{
context.Entry(testType).State = EntityState.Modified;
}
context.Attach(schoolclass);
context.Attach(subject);
context.Attach(schoolyear);
await context.SaveChangesAsync();
return await this.GetTestTypesConfigurationAsync(schoolclassId, subjectId);
}
public class TestTypeComparer : IEqualityComparer<TestType>
{
public bool Equals(TestType x, TestType y)
{
return x.Id == y.Id;
}
public int GetHashCode(TestType obj)
{
return obj.Id.GetHashCode();
}
}
public class TestType
{
public TestType()
{
Tests = new HashSet<Test>();
}
public int Id { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
public ISet<Test> Tests { get; set; }
public Schoolyear Schoolyear { get; set; }
public Schoolclass Schoolclass { get; set; }
public int SchoolclassId { get; set; }
public Subject Subject { get; set; }
public int SubjectId { get; set; }
public int SchoolyearId { get; set; }
}
任何人都可以帮助我,我无法发现具有相同密钥的双重跟踪实体。
我只是假设问题与我如何确定添加/修改/删除的实体有关。
更新
我现在已经在为所有 TestTypesToUpdate 设置 State.Modified 之前记录了所有跟踪器更改:
State: Deleted | Type: TestType | Id-Value: 12
State: Unchanged | Type: Schoolclass | Id-Value: 1
State: Unchanged | Type: TestType | Id-Value: 8
State: Unchanged | Type: Subject | Id-Value: 1
State: Deleted | Type: TestType | Id-Value: 13
State: Added | Type: TestType | Id-Value: -2147482647
State: Detached | Type: Schoolclass | Id-Value: 1
State: Added | Type: Schoolyear | Id-Value: 1
State: Detached | Type: Subject | Id-Value: 1
State: Added | Type: TestType | Id-Value: -2147482646
似乎是的......一些实体已经被追踪。看来我必须在更改跟踪器上投入更多的 if/else,然后再决定要做什么。
但我不敢相信我是第一个这样做的人,在谷歌上没有找到任何东西。
【问题讨论】:
-
IMO 这是因为上下文。您应该在每个查询中处理并重新初始化。但是您不止一次使用相同的上下文。
-
在您看来,从技术上讲,什么是多次使用?您是否在方法结束时谈论 Get 调用?我可以删除那个 Get Call 而不会产生任何影响。
-
"context.TestTypes" 上下文来自哪里?每次调用时“上下文”都是新的吗? Dispose
-
这个上下文被注入到每个 http 调用的存储库构造函数中。它只是创建了一次!