我正在寻找使用现代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 => ...)
。
答案 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
类型。