無法跟踪實體類型“TestType”的實例,因為已經跟踪了具有相同鍵的此類型的另一個實例

c# entity-framework-core

我嘗試這樣做時遇到以下異常:

    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();加載.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; }
    }

任何人都可以幫助我,我不能用相同的密鑰發現雙軌實體。

我只是假設問題有關如何確定添加/修改/刪除實體。

UPDATE

在我為所有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,然後決定做什麼。

但我無法相信我是第一個做這樣的事情,在谷歌上找不到任何東西。

一般承認的答案

在考慮具有相同鍵問題的多個實例後,我再次重新排列代碼,直到我再次得到錯誤,我無法將testTypeToUpdate的狀態設置為state.Modified,因為SchoolclassId是鍵的一部分。

嗯...然後我搜索了我的流暢遷移並發現了這個:

modelBuilder.Entity<TestType>().HasAlternateKey(x => new { x.Name, x.SchoolclassId, x.SubjectId });

我之前做過這個(但從來沒有對它進行測試 - 對我感到羞恥 - )我認為它的EF6相當於索引屬性...因為這就是我想要的功能!

然後我用Google搜索並創建了一些新東西:

modelBuilder.Entity<TestType>().HasIndex(p => new { p.Name, p.SchoolclassId, p.SubjectId} ).IsUnique();

現在我有我想要的東西!

我還將schoolclass,subject和schoolyear實例的分配刪除到testTypesToRemove,它也創建了一些奇怪的東西......

該代碼現在有效:

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

            // Make the navigation properties available during SaveChanges()
            context.Attach(schoolclass);
            context.Attach(subject);
            context.Attach(schoolyear);

            // DELETE
            var testTypesToRemove = testTypesFromDatabase.Except(testTypes, new TestTypeComparer()).ToList();
            context.TestTypes.RemoveRange(testTypesToRemove);

            // ADD
            var testTypesToAdd = testTypes.Where(t => t.Id == 0).ToList();  // 
            foreach (var testType in testTypesToAdd)
            {
                testType.Schoolclass = schoolclass;
                testType.Subject = subject;
                testType.Schoolyear = schoolyear;
            }
            context.TestTypes.AddRange(testTypesToAdd);

            // UPDATE
            var modifiedTestTypesToUpdate = testTypes.Except(testTypesToAdd.Concat(testTypesToRemove).ToList(), new TestTypeComparer()).ToList();
            foreach (var testType in modifiedTestTypesToUpdate)
            {
                testType.Schoolclass = schoolclass;
                testType.Subject = subject;
                testType.Schoolyear = schoolyear;
            }   
            context.UpdateRange(modifiedTestTypesToUpdate);

            await context.SaveChangesAsync();

            return await this.GetTestTypesConfigurationAsync(schoolclassId, subjectId);
        }


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow