2016-12-16 69 views
0

我得到下面的異常,當我嘗試這樣做:實體類型「TestType」的實例無法被跟蹤,因爲這種類型的使用相同的密鑰的另一個實例已被跟蹤

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實例已被跟蹤!

從數據庫中testtypes通過.AsNoTracking();

加載所以在這段代碼與在那裏我在做一個RemoveRange /操作的AddRange相同的密鑰TestType的實例?那意味着我的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

我現在已經登錄之前我設置State.Modified所有TestTypesToUpdate的所有跟蹤的變化:

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 

這似乎是啊...一些實體已經跟蹤。似乎我必須在變更跟蹤器上投入更多的費用,然後決定做什麼。

但我不敢相信我是第一個做這樣的事情,沒有發現任何東西在谷歌。

+0

IMO這是因爲語境。您應該在每個查詢中處理並重新初始化。但是你多次使用相同的上下文。 – BMaximus

+0

什麼意思是在你的意見中多次使用技術?在方法結尾處是否談到Get調用?我可以在沒有任何影響的情況下移除Get Call。 – Elisabeth

+0

「context.TestTypes」其中的上下文來自哪裏?每次你打電話時都是「情境」嗎? [Dispose](http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html) – BMaximus

回答

0

具有相同的密鑰問題思考多個實例後,我又有點重排的代碼,直到我再次得到了錯誤,我不能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,主題和學年實例,也創造了一些奇怪的東西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); 
     } 
相關問題