无法在TPL下查询EF上下文

时间:2010-12-20 09:55:59

标签: c#-4.0 entity-framework-4 task-parallel-library

我有一个过程,我从数据库中读取数千条记录,将每个记录编码为单独的XML消息,并将所述消息发送到WCF服务。

通过EF4模型引用数据库。我正在使用TPL来并行化XML消息的创建。第一个LINQ查询出现问题:

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没有为类似查询结果分配新密钥,这意味着如果查询相同结果的同一个表,则会发生上述异常(因为结果是相同的)的datacontext)。

因为我正在使用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

2 个答案:

答案 0 :(得分:1)

  

经过大量研究后我发现EF没有为类似查询结果分配新密钥,这意味着如果查询相同结果的同一个表,则会发生上述异常(因为结果是相同的)的datacontext)。

那不对。可以多次运行同一个查询。在测试应用程序中试用它。

如果在上下文中选择相同的对象两次,则默认情况下,实例将被修复为同一个对象。请参阅ObjectQuery.MergeOption的文档。

通常看到AddObject()两次使用同一个对象时给出的错误。

我认为您的错误可能在其他地方。

顺便说一句,我会写这样的查询:

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

不应该对这个问题产生任何影响。

答案 1 :(得分:0)

我有同样的问题,在阅读了carig回答之后我会用一些锁来处理它......

它以某种方式工作,我不知道多少,但上次我在第3次或第4次多线程应用程序后得到错误,同步进入该部分,但这一次,它没有大约10次(测试调试)(顺便说一句,我没有再面对那个错误),...

我有2个线程同步检查数据库中是否有可用的工作......然后他们同时寻找工作,因为数据库的滞后直到它响应,所以我认为它能完成这项工作。

enter image description here

正如你所看到的那样,线程正在等待锁定,因此EF每次只处理一个相同的查询,并且它会阻止错误......

更好的方法是那些线程到达缓存列表,一段时间之后你让线程触发另一个线程,它将用新数据填充缓存,所以你总是有最新的数据,你不要把数据库放在获取相同数据的压力,因此锁定可以消失......(如果您不需要确切的最新数据)