2016-09-30 39 views
1

再添雙我有具有以下方法庫基類:奇怪的實體框架的行爲 - 當IEnumerable不能列出

public void AddRange(IEnumerable<TEntity> entities) 
     { 
      Context.Set<TEntity>().AddRange(entities); 
      foreach (var entity in entities) 
      { 
       if (Context.Entry(entity).State == EntityState.Detached) 
       { 
        Context.Entry(entity).State = EntityState.Added; 
       } 
      } 
     } 

我叫它像這樣:

uow.UserPermissions.AddRange(permissions); 

其中UserPermissions是從基地繼承的存儲庫。

我看到一些奇怪的行爲,當權限不是列表時我無法解釋。例如,當我嘗試這樣:

var permissions = permissionDtos.Select(dto => new UserPermission() 
       { 
        ... 
       }); 

       uow.UserPermissions.AddRange(permissions); 

實體框架儘可能多的權限分貝因爲有permissionDtos增加了兩倍。但是,如果我在select語句的末尾添加了ToList(),那麼奇怪的行爲就會消失。我還注意到,當我在Repository.AddRange()方法中註釋掉forEach循環(即修改上下文條目狀態)時,奇怪的行爲也會消失(即使不添加ToList())。

在此先感謝。

回答

1

你做錯了,這就是爲什麼這種行爲。當你這樣做Context.Set<TEntity>().AddRange(entities);時,它將給定的實體集合添加到集合的上下文中,每個實體都被放入添加狀態,這樣它將被插入當SaveChanges被調用時,進入數據庫。您可以在MSDN上看到它。 所以你不需要在foreach循環內再次做它。當你刪除任何一個時,你的方法應該是OK的。

您可以嘗試如下所示。

public void AddRange(IEnumerable<TEntity> entities) 
     { 
      Context.Set<TEntity>().AddRange(entities); 

     } 
0

爲了詳細說明爲什麼你得到兩倍的條目,這是因爲IQueryable<T>的工作原理。

每次枚舉查詢時,查詢都會重新執行。事實上,查詢實際上並沒有實際執行,直到它到達循環內某處Context.Set<TEntity>().AddRange(entities);

在內部,AddRange枚舉查詢結果,在您的情況下,它將選擇permissionDtos後面的表中的所有行,並將它們投影到新的UserPermission對象。

然後,在您的foreach循環中,通過再次枚舉查詢來再次執行查詢,該查詢再次選擇所有行並將它們投影爲新的UserPermission對象。由於您正在預測這些結果,因此框架無法重用在第一次執行查詢期間創建的對象。如果您直接枚舉兩次Set<TEntity>,它仍然會執行兩次查詢,但它可以重新使用已經物化的對象。

如果您使用ToList(),則會迫使查詢和投影立即發生,並且您不再保存查詢本身 - 在這種情況下,permissions不再是查詢,而是已經存在的查詢的列表,預計項目。

Sampath已經回答說你的foreach循環是多餘的 - 將實體添加到上下文中會將它們標記爲已添加,並且設置添加的狀態會將它們添加到上下文中(兩者之間唯一的區別在於如何關聯通過導航屬性訪問的實體被處理)。即使在List情況下,你也在做一個不必要的循環。