2014-02-06 6 views
2

SQL Server 11.0.3, C# .NET 4.5, WCF, EF v4.0.30319ExecuteSqlCommand параллельное выполнение

У меня есть следующая таблица, в которой хранить запросы:

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., то я попробуйте еще раз get Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

Имею ли я это правильно? Любые предложения?

+2

Ну, для одного я не думаю, что один 'SqlConnection' может использоваться из нескольких потоков одновременно, даже с MARS. И это 'Parallel.ForEach' поместит' ExecuteSqlCommand' в другой поток, который может сделать этот «TransactionScope» бесполезным. –

+0

ОК, так что я могу просто предположить, что это невозможно? – Yann39

ответ

0

Я могу дать абстрактный ответ на это, он, вероятно, не будет напрямую отвечать на ваш вопрос (и у меня нет ответа, чтобы опубликовать комментарий вместо этого), но это по-прежнему ценная информация для этого типа проблем ,

Есть несколько проблем с тем, что вы пытаетесь сделать.

Первый - контексты базы данных (и расширениями SQL-соединений) и параллелизма потоков. Вы абсолютно не можете делиться соединением по потокам, вы должны создать новое соединение для каждого потока, поскольку оно возникает параллельно. На самом деле это не ошибка, связанная с пулом соединений, поэтому ваш рейтинг производительности минимален. Поэтому в вашем случае вам нужно создать экземпляр контекста внутри блока параллелизма.

Во-вторых, выполнение параллельных запросов в базе данных внутри транзакции, как правило, не является хорошей стратегией. Причина этого заключается в том, что при запуске нескольких одновременных запросов в одной и той же базе данных вы должны помнить о своем режиме изоляции. В зависимости от подключения и глобальных настроек вы, вероятно, будете забивать свой журнал/журнал и вызывать код DTC. Если весь ваш набор запросов должен быть безопасным для транзакций в целом, тогда вы не будете пользоваться вообще, выполнив запросы параллельно, и вместо этого следует выполнять каждый запрос последовательно. Если только каждый отдельный запрос должен быть безопасным для транзакций, тогда вы должны создать контекст транзакции в блоке параллелизма.

Последним является только предложение использовать Task вместо Parallel. наличие вложенной структуры задач, которые могут выполняться параллельно, создает немного более организационный смысл, чем выполнение параллельных параллельных блоков в цикле. В этом случае у вас будет основная задача (которую вы могли бы использовать Wait()), которая имела бы дочерние задачи для каждого OBJECT_TYPE. Вы можете создать свою структуру Task, итерации запроса GroupBy в вашем наборе данных запроса. Как сказано ранее, это всего лишь предложение и не обязательно нужно выполнять параллельное выполнение запросов.

Что-то еще нужно учитывать (аналогично стратегии Task), заключается в использовании асинхронных методов для выполнения каждого запроса, а затем простой функции для вызова каждого метода асинхронизации и ожидания в конце. Просто мысль.

Смежные вопросы