2010-12-20 18 views
2

我有一個過程,我從數據庫中讀取上千條記錄下查詢EF背景下,每一個編碼成一個單獨的XML消息,併發送所述消息開了一個WCF服務。不能TPL

數據庫通過EF4模型引用。我正在使用TPL來並行化XML消息的創建。

var practice = (from patient in db.T_AccountHolder 
       join practitioner in db.T_Practitioner on patient.DefaultPractitioner_ID equals practitioner.Practitioner_ID 
       join _practice in db.T_Practice on practitioner.Practice_ID equals _practice.Practice_ID 
       where patient.AccountHolder_ID == accountholder_id 
       select _practice).FirstOrDefault(); 

我得到以下異常:

ArgumentException: An item with the same key has already been added. 

經過大量的研究,我發現,EF不分配一個新的關鍵,同樣查詢結果與第一個LINQ查詢時出現問題,這意味着如果您查詢具有相同結果的同一個表,則會發生上述異常(因爲結果在同一個數據上下文中)。

由於我使用TPL,我在那確切情況。我唯一的辦法是不使用EF嗎?回到正常的ADO.NET查詢?

我搜索上下這個優秀的網站和谷歌,但似乎無法找到一個類似類型的問題。


編輯:這是一個錯誤的堆棧跟蹤。

Exception message: An item with the same key has already been added. 

at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) 
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) 
at System.Data.Objects.ObjectStateManager.AddStateManagerTypeMetadata(EntitySet entitySet, ObjectTypeMapping mapping) 
at System.Data.Objects.ObjectStateManager.GetOrAddStateManagerTypeMetadata(Type entityType, EntitySet entitySet) 
at System.Data.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded) 
at System.Data.Common.Internal.Materialization.Shaper.HandleEntityAppendOnly[TEntity](Func`2 constructEntityDelegate, EntityKey entityKey, EntitySet entitySet) 
at lambda_method(Closure , Shaper) 
at System.Data.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper shaper) 
at System.Data.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext() 
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source) 
at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__1[TResult](IEnumerable`1 sequence) 
at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot) 
at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression) 
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source) 
at WCFServiceTest.Messages.CreateAccountHolderMessage(Int32 accountholder_id) in C:\Users\Chris\documents\visual studio 2010\Projects\WCFServiceTest\WCFServiceTest\Messages.cs:line 116 
at WCFServiceTest.Messages.CreateParallelMessagesForAccountHolder(Int32 accountholder_id, manmayEntities _db, List`1 queue) in C:\Users\Chris\documents\visual studio 2010\Projects\WCFServiceTest\WCFServiceTest\Messages.cs:line 2482 
at WCFServiceTest.ParallelWork.<>c__DisplayClass22.<ProcessData_EF>b__1f(Int32 patient_id) in C:\Users\Chris\documents\visual studio 2010\Projects\WCFServiceTest\WCFServiceTest\ParallelWork.cs:line 298 
+0

指定每個線程的新上下文不是回答。今天早上試過並得到了類似的結果(「實體已經存在」類型的錯誤)。 – Chris 2010-12-21 06:33:40

回答

1

經過大量的研究,我發現,EF不分配一個新的關鍵,同樣查詢結果,這意味着如果查詢相同的表具有相同的結果,就會出現上述異常(因爲結果是在相同的數據上下文中)。

這是不對的。多次運行同一個查詢就好了。在測試應用程序中試用它。

如果選擇同一對象爲背景的兩倍,默認情況下會被固定起來是同一個對象。請參閱ObjectQuery.MergeOption的文檔。

通常看到你給出的錯誤,當你用相同的對象兩次AddObject()

我認爲你的bug可能在其他地方。

BTW,我會寫你的查詢是這樣的:

var practice = (from patient in db.T_AccountHolder 
       where patient.AccountHolder_ID == accountholder_id 
       select patient.Practitioner.Practice).FirstOrDefault(); 

不應該對這個問題有什麼區別,雖然。

+0

感謝您的回覆。然而,這是我正在嘗試的一個測試應用程序,並且多個線程(通過TPL框架)確實會導致規定的異常。按順序運行相同的代碼不會導致錯誤。 – Chris 2010-12-22 13:50:08

+0

我明白,但我仍然不認爲這條線是造成錯誤。你看過與異常相關的堆棧跟蹤嗎?你可以發佈嗎? – 2010-12-22 14:23:08

+0

已添加請求的堆棧跟蹤。 – Chris 2010-12-23 10:32:21

0

我同樣的問題,閱讀carig回答的想法來,我可以使用一些鎖處理它...

它在某種程度上工作後,我不知道有多少,但我最後一次得到一個錯誤3TH或第四次的多線程應用程序後,進入的那部分同步,但這個時候,它並沒有爲約10倍(測試調試)(順便說一句,我沒有再次面對這個錯誤),...

我有2個線程同步檢查數據庫的可用作業...然後他們同時查找作業,因爲數據庫的滯後時間直到它響應,所以我認爲它完成了這項工作。

enter image description here

正如你所看到的線程都在等待,在鎖定的,所以EF每次只處理一個相同的查詢,並停止錯誤...

更好的辦法將是那些線程到達緩存列表,在一秒之後,讓線程觸發另一個線程,它將用新數據填充緩存,因此您始終擁有最新數據,並且不會讓數據庫承受獲取相同數據的壓力,因此鎖可以走開...(如果你不需要確切的最新數據)