与实体框架并行不工作 [英] Parallel doesnt work with Entity Framework

查看:137
本文介绍了与实体框架并行不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有ID的列表,而且我需要在每个ID运行多个存储过程。

I have a list of IDs, and I need to run several stored procedures on each ID.

在我使用的是标准的foreach循环,它工作正常,但是当我有很多的记录,它的工作原理pretty的慢。

When I am using a standard foreach loop, it works OK, but when I have many records, it works pretty slow.

我想转换code与EF工作,但我得到一个异常:基础提供程序未能在打开

I wanted to convert the code to work with EF, but I am getting an exception: "The underlying provider failed on Open".

我用这code时,Parallel.ForEach里面:

I am using this code, inside the Parallel.ForEach:

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

但它仍然抛出异常。

But it still throws the exception.

任何想法,我该如何使用并行与EF?我需要为我运行的每一个程序一个新的环境?我有大约10程序,所以我认为它很不好创建10个环境,每个。

Any idea how can I use Parallel with EF? do I need to create a new context for every procedure I am running? I have around 10 procedures, so I think its very bad to create 10 contexts, one for each.

推荐答案

的实体框架所使用的底层数据库连接的不是的是线程安全的。您的将会的需要建立在您打算执行另一个线程的每个操作一个新的环境。

The underlying database connections that the Entity Framework are using are not thread-safe. You will need to create a new context for each operation on another thread that you're going to perform.

您如何并行化操作关心的是一个有效的;许多上下文将是昂贵的,以打开和关闭。

Your concern about how to parallelize the operation is a valid one; that many contexts are going to be expensive to open and close.

相反,你可能要反转您如何对并行化code思考。看来你遍历多个项目,然后调用串行存储过程的每一个项目。

Instead, you might want to invert how your thinking about parallelizing the code. It seems you're looping over a number of items and then calling the stored procedures in serial for each item.

如果可以的话,创建一个新的 任务&LT; TResult&GT; (或<一href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx"><$c$c>Task,如果你不需要一个结果)的每个的程序的,然后在任务&LT; TResult&GT; ,全部通过打开一个单一的背景下,循环该项目,然后执行该存储过程。这样一来,你只有一个数字的上下文等于你在并行运行存储过程的次数。

If you can, create a new Task<TResult> (or Task, if you don't need a result) for each procedure and then in that Task<TResult>, open a single context, loop through all of the items, and then execute the stored procedure. This way, you only have a number of contexts equal to the number of stored procedures that you are running in parallel.

让我们假设你有一个 MyDbContext 有两个存储过程, DoSomething1 了doSomething2 ,两者都需要一个类的实例, MyItem

Let's assume you have a MyDbContext with two stored procedures, DoSomething1 and DoSomething2, both of which take an instance of a class, MyItem.

实现上述看起来是这样的:

Implementing the above would look something like:

// 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.

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

If you can't execute the stored procedures in parallel (each one is dependent on being run in a certain order), then you can still parallelize your operations, it's just a little more complex.

您会看创建自定义分区在您的项目(使用静态<一href="http://msdn.microsoft.com/en-us/library/system.collections.concurrent.partitioner.create.aspx"><$c$c>Create对<方法 href="http://msdn.microsoft.com/en-us/library/system.collections.concurrent.partitioner.aspx"><$c$c>Partitioner类)。这将使你获得 的IEnumerator&LT的手段; T&GT; 实现(注意,这是的没有 的IEnumerable&LT; T&GT; 所以你不能的foreach 过它)

You would look at creating custom partitions across your items (using the static Create method on the Partitioner class). This will give you the means to get IEnumerator<T> implementations (note, this is not IEnumerable<T> so you can't foreach over it).

对于每个的IEnumerator&LT; T&GT; 比如你回来,你会创建一个新的任务&LT; TResult&GT; (如果你需要一个结果),并在任务&LT; TResult&GT; 的身体,你需要创建通过返回的项目范围内,然后循环 IEnumerator的&LT; T&GT; ,调用顺序存储过程

For each IEnumerator<T> instance you get back, you'd create a new Task<TResult> (if you need a result), and in the Task<TResult> body, you would create the context and then cycle through the items returned by the IEnumerator<T>, calling the stored procedures in order.

这是这样的:

// 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();

这篇关于与实体框架并行不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆