gpt4 book ai didi

c# - 并行不适用于 Entity Framework

转载 作者:IT王子 更新时间:2023-10-29 04:37:10 26 4
gpt4 key购买 nike

我有一个 ID 列表,我需要对每个 ID 运行多个存储过程。

当我使用标准的 foreach 循环时,它工作正常,但是当我有很多记录时,它工作得非常慢。

我想将代码转换为使用 EF,但出现异常:“基础提供程序在打开时失败”。

我在 Parallel.ForEach 中使用这段代码:

using (XmlEntities osContext = new XmlEntities())
{
//The code
}

但它仍然抛出异常。

知道如何将 Parallel 与 EF 一起使用吗?我是否需要为我正在运行的每个过程创建一个新的上下文?我有大约 10 个过程,所以我认为创建 10 个上下文非常糟糕,每个上下文一个。

最佳答案

The underlying database connections that the Entity Framework are using are not thread-safe .您需要为您将要执行的另一个线程上的每个操作创建一个新上下文。

您对如何并行化操作的担忧是有道理的;许多上下文的打开和关闭成本很高。

相反,您可能想要颠倒您对代码并行化的想法。看起来您正在遍历多个项目,然后为每个项目连续调用存储过程。

如果可以,创建一个新的 Task<TResult> (或 Task ,如果您不需要结果)每个过程 然后在那个 Task<TResult> 中,打开一个上下文,遍历所有项目,然后执行存储过程。这样,您的上下文数量就等于您并行运行的存储过程的数量。

假设您有一个 MyDbContext有两个存储过程,DoSomething1DoSomething2 ,两者都采用一个类的实例,MyItem .

实现上面的代码看起来像这样:

// You'd probably want to materialize this into an IList<T> to avoid
// warnings about multiple iterations of an IEnumerable<T>.
// You definitely *don't* want this to be an IQueryable<T>
// returned from a context.
IEnumerable<MyItem> items = ...;

// The first stored procedure is called here.
Task t1 = Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Cycle through each item.
foreach (MyItem item in items)
{
// Call the first stored procedure.
// You'd of course, have to do something with item here.
ctx.DoSomething1(item);
}
});

// The second stored procedure is called here.
Task t2 = Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Cycle through each item.
foreach (MyItem item in items)
{
// Call the first stored procedure.
// You'd of course, have to do something with item here.
ctx.DoSomething2(item);
}
});

// Do something when both of the tasks are done.

如果您不能并行执行存储过程(每个存储过程都依赖于按特定顺序运行),那么您仍然可以并行化您的操作,只是稍微复杂一点。

你会看creating custom partitions跨越你的项目(使用 Create method 上的静态 Partitioner class )。这将为您提供获得 IEnumerator<T> 的方法实现(注意,这不是 IEnumerable<T> 所以你不能 foreach 超过它)。

对于每个 IEnumerator<T>如果你回来,你会创建一个新的 Task<TResult> (如果您需要结果),并在 Task<TResult>正文,您将创建上下文,然后循环浏览 IEnumerator<T> 返回的项目, 按顺序调用存储过程。

看起来像这样:

// Get the partitioner.
OrdinalPartitioner<MyItem> partitioner = Partitioner.Create(items);

// Get the partitions.
// You'll have to set the parameter for the number of partitions here.
// See the link for creating custom partitions for more
// creation strategies.
IList<IEnumerator<MyItem>> paritions = partitioner.GetPartitions(
Environment.ProcessorCount);

// Create a task for each partition.
Task[] tasks = partitions.Select(p => Task.Run(() => {
// Create the context.
using (var ctx = new MyDbContext())
// Remember, the IEnumerator<T> implementation
// might implement IDisposable.
using (p)
// While there are items in p.
while (p.MoveNext())
{
// Get the current item.
MyItem current = p.Current;

// Call the stored procedures. Process the item
ctx.DoSomething1(current);
ctx.DoSomething2(current);
}
})).
// ToArray is needed (or something to materialize the list) to
// avoid deferred execution.
ToArray();

关于c# - 并行不适用于 Entity Framework ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12827599/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com