并行不适用于Entity Framework

m0f*_*0fo 21 .net c# parallel-processing multithreading entity-framework

我有一个ID列表,我需要在每个ID上运行几个存储过程.

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

我想将代码转换为使用EF,但我得到一个例外:"底层提供程序在Open上失败".

我在Parallel.ForEach中使用此代码:

using (XmlEntities osContext = new XmlEntities())
{
    //The code
}
Run Code Online (Sandbox Code Playgroud)

但它仍然抛出异常.

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

cas*_*One 38

实体框架使用的基础数据库连接不是线程安全的.你需要在另一个线程,你要执行为每个操作的新的上下文.

您对如何并行化操作的关注是有效的; 开启和关闭的许多背景都很昂贵.

相反,您可能想要反思您对并行化代码的看法.看起来你正在循环遍历许多项目,然后为每个项目串行调用存储过程.

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

假设你有MyDbContext两个存储过程,DoSomething1并且两个存储过程DoSomething2都是一个类的实例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.
Run Code Online (Sandbox Code Playgroud)

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

您将看到在项目中创建自定义分区(使用上的静态Create方法).这将为您提供获得实现的方法(注意,这不是你不能超过它).PartitionerIEnumerator<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();
Run Code Online (Sandbox Code Playgroud)


kev*_*itz 6

EF不是线程安全的,因此您不能使用Parallel.

看一下Entity Framework和Multi threading

和这篇文章.