实体框架4和Async / TPL / Multi Threaded调用

时间:2014-12-05 19:20:40

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

我正在寻找使用现代TPL(任务并行库)调用Entity Framework 4模型的一些建议。我很欣赏这看起来在EF6中实现,但还没有选择升级到Entity Framework 6。我希望在我的代码中可以做一些事情来管理这个,我只是想要掌握TPL。

我通常调用从TreeView触发的负载。但是,树的性质是调用相同EntityObject类型的数据访问。我在Node Expanded事件中执行以下操作:

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
                ExplorerViewModel.GetContracts(parentNode.Tag as Project).ToList())
                .ContinueWith(cw =>
                {
                    PopulateContracts(parentNode, cw.Result);
                }, synchronizationContext);

ExplorerViewModel.GetContracts将调用我的DbContext。 ExplorerViewModel是Windows窗体中的单个实例。 ExplorerViewModel有一个DbContext实例。

抛出以下异常:

System.ArgumentException was unhandled by user code
  HResult=-2147024809
  Message=An item with the same key has already been added.
  Source=mscorlib
  StackTrace:
       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.Data.Objects.DataClasses.RelatedEnd.Merge[TEntity](IEnumerable`1 collection, MergeOption mergeOption, Boolean setIsLoaded)
       at System.Data.Objects.DataClasses.EntityCollection`1.Load(List`1 collection, MergeOption mergeOption)
       at System.Data.Objects.DataClasses.EntityCollection`1.Load(MergeOption mergeOption)
       at System.Data.Objects.DataClasses.RelatedEnd.Load()
       at ACMEDev.MyApp.EFDAL.Entity.Project.get_ContractList() in k:\TFS\MyApp\Current\Data\source\ACMEDev.MyApp.EFDAL\Entity\Project.Generated.cs:line 667
       at ACMEDev.MyApp.Data.Entity.MyAppRepository.GetContracts(Project project, MyAppEntities context) in k:\TFS\MyApp\Current\MyApp\source\ACMEDev.MyApp.Data\Entity\MyAppRepository.cs:line 43
       at ACMEDev.MyApp.ViewModels.MainExplorerViewModel.GetContracts(Project project) in k:\TFS\MyApp\Current\MyApp\source\ACMEDev.MyApp.ViewModels\MainExplorerViewModel.cs:line 611
       at ACMEDev.MyApp.Controls.Navigation.MyAppExplorerTree.<>c__DisplayClass17.<PopulateNode>b__4() in k:\TFS\MyApp\Current\MyApp\source\ACMEDev.MyApp.Controls\Navigation\MyAppExplorerTree.cs:line 347
       at System.Threading.Tasks.Task`1.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()
  InnerException: 

检索到大量数据,因此负载可能很慢。我将在树上显示一个忙碌的指示器,将其标记给用户,但我正在测试我输入的异步部分。

我可以在我的存储库lock { }的{​​{1}}方法中使用static,但想知道是否有一个很好的方法。这不适用于调用关系对象,您可以在MyAppRepository的其他情况下调用parent.ChildrenList.Where(x => ...)

1 个答案:

答案 0 :(得分:0)

  

ExplorerViewModel是Windows窗体中的单个实例。   ExplorerViewModel有一个DbContext实例。

听起来这只有static的{​​{1}}个实例,这可能会导致问题。您是否可以让DbContext方法为每次调用创建一个GetContracts实例?

你通常不应该有很长时间的EF DbContext。将DbContext实例声明为new DbContext字段或属性通常是一个非常糟糕的主意,这会导致您在上面和其他人中描述的问题。原因是static成员只有一个由所有协作者共享的实例。当发生这种情况时,您会遇到穷人static的并发问题,这会为太多客户端做太多事情。

所以,而不是:

DbContext

...尝试这样的事情:

public class ExplorerViewModel
{
    private static SomeDbContext _myDbContext = new SomeDbContext();

    public static object GetContracts(Project project)
    {
        _myDbContext.DoSomething();
        ...
        return result;
    }
}

这样,每个线程都会获得自己的DbContext实例,当线程使用它时,该实例会被正确处理掉。

  

我可以在我的存储库的静态方法中使用lock {}   MyAppRepository,但想知道是否有一个好方法。

&#34; nice&#34;方法是在public class ExplorerViewModel { public static object GetContracts(Project project) { using (var myDbContext = new SomeDbContext()) { myDbContext.DoSomething(); ... return result; } } } 上没有任何static方法。相反,使用对象的实例,并让每个范围获得自己的实例。听起来你在滥用MyAppRepository类型。