ExecuteSqlCommand并行执行

时间:2014-02-06 09:52:45

标签: c# sql-server linq entity-framework parallel.foreach

SQL Server 11.0.3,C#.NET 4.5,WCF,EF v4.0.30319

我有下表存储查询:

OBJECT_TYPE     OBJECT_NAME         OBJECT_TYPE_NAME    OBJECT_QUERY
1               HUB_PERSON          HUB                 MERGE INTO ...
1               HUB_GPS             HUB                 MERGE INTO ...
2               LNK_PERSON_GPS      LNK                 MERGE INTO ...
2               SAT_CURR_PERSON     SAT                 MERGE INTO ...
2               SAT_GPS             SAT                 MERGE INTO ...
2               SAT_HIST_PERSON     SAT                 MERGE INTO ...

我必须按照OBJECT_TYPE列指定的顺序从我的服务执行查询。具有相同OBJECT_TYPE的查询可以按任何顺序执行,这就是为什么我想在可能的情况下并行执行这些查询的原因。

这是我目前使用的功能,没有并行性:

public int ExecuteLoadSources()
        {
            int nb = 0;
            using (POCDVEntities POCDb = new POCDVEntities())
            {
                using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required))
                {
                    IEnumerable<string> res = (
                        from a in POCDb.LOADING_SOURCE_QUERY
                        orderby a.OBJECT_TYPE
                        select a.OBJECT_SQLSRV_QUERY
                    );
                    foreach (string req in res)
                    {
                        POCDb.Database.ExecuteSqlCommand(req);
                    }
                    nb = POCDb.SaveChanges();
                    transaction.Complete();
                }
            }
            return nb;
        }

我试过Parallel.ForEach这样:

public int ExecuteLoadSources()
        {
            int nb = 0;
            using (POCDVEntities POCDb = new POCDVEntities())
            {
                using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required))
                {
                    IEnumerable<string> res = (
                        from a in POCDb.LOADING_SOURCE_QUERY
                        select a.OBJECT_SQLSRV_QUERY
                    );
                    int maxValue = (from a in POCDb.LOADING_SOURCE_QUERY select a.OBJECT_TYPE).Max();
                    for (int i = 1; i <= maxValue; i++)
                    {
                        IEnumerable<string> res2 = (
                            from a in POCDb.LOADING_SOURCE_QUERY
                            where a.OBJECT_TYPE == i
                            select a.OBJECT_SQLSRV_QUERY
                        ).ToList();
                        Parallel.ForEach(res2, req2 =>
                        {
                            POCDb.Database.ExecuteSqlCommand(req2);
                        });
                    }
                    nb = POCDb.SaveChanges();
                    transaction.Complete();
                }
            }
            return nb;
        }

我第一次运行该功能时,得到The underlying provider failed on Open.,然后我再次尝试获取Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

我做得对吗? 有什么建议吗?

1 个答案:

答案 0 :(得分:0)

我可以给出一个抽象的答案,它可能不会直接回答你的问题(而且我没有让代表发表评论)但它仍然是有价值的信息。问题的类型。

您尝试做的事情有几个问题。

第一个是数据库上下文(以及扩展的sql连接)和线程并发。您绝对无法跨线程共享连接,您必须在每个线程上并行生成时创建新连接。这实际上不是因为连接池而导致的错误处理,所以无论如何你的性能都很小。因此,在您的情况下,您需要在并发块内创建上下文实例。

第二,在事务内部对数据库执行并行查询通常不是一个好策略。原因是当您在同一个数据库上运行多个同时查询时,您需要注意隔离模式。根据连接和全局设置,您可能会锤击日志/日志并调用DTC。如果您的整个查询集必须在整个事务上是安全的,那么您将无法通过并行运行查询而受益,而应该依次运行每个查询。如果只有每个单独的查询必须是事务安全的,那么您应该在并发块中创建事务上下文。

最后一个建议是使用Task代替Parallel。具有可以并行运行的任务的嵌套结构比在循环中执行并行并发块具有更多的组织意义。在这种情况下,您将拥有一个主任务(您可以Wait()开启),该任务将为每个OBJECT_TYPE创建子任务。您可以通过在查询数据集上迭代Task查询来生成GroupBy结构。就像之前所说的那样,这只是一个建议,并不是完成查询并行执行所必需的。

要考虑的其他事情(类似于Task策略),是使用异步方法来执行每个查询,然后使用一个简单的函数来调用每个异步方法并在最后等待所有异步方法。只是一个想法。