Задачи и хранимые процедуры TPL - PullRequest
2 голосов
/ 10 августа 2011

Мне было интересно, можно ли асинхронно вызывать несколько разных хранимых процедур с одинаковыми параметрами, используя задачи, а затем ожидая возврата всех результатов.

У меня есть следующее:

private Task<DataTable> DataBaseCall(string procedureName, params Pair[] where)
    {
        DataTable data = new DataTable();
        SqlConnection connection = new SqlConnection(connStr);

        SqlCommand command = new SqlCommand(procedureName, connection);
        connection.Open();

        for (int i = 0; i < where.Length; i++)
        {
            command.Parameters.Add(where[i].First.ToString(), where[i].Second.ToString());
        }

        var readerTask = Task<SqlDataReader>.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null);
        return readerTask.ContinueWith(t =>
            {
                var reader = t.Result;
                try
                {
                    reader.Read();
                    data.Load(reader);
                    return data;
                }
                finally
                {
                    reader.Dispose();
                    command.Connection.Close();
                    command.Connection.Dispose();
                    command.Dispose();
                }
            });
    }

Которым я звоню:

private void SetReportVariables(string reportName, string[] storedProcedureName, string _clientGroup, string _clientCode, string _finYear, string _period)
    {
       Task[] tasks = new Task[storedProcedureName.Length];

        for (int i = 0; i < storedProcedureName.Length; i++)
        {
            List<Pair> parameters = new List<Pair>();
            parameters.Add(new Pair("@ClientGroup", _clientGroup));
            parameters.Add(new Pair("@ClientCode", _clientCode));
            parameters.Add(new Pair("@FinYear", _finYear));

            tasks[i] = DataBaseCall(storedProcedureName[i], parameters.ToArray());
        }
        Task.WaitAll(tasks);

        ...........Do something with the DataTables.........
    }

У меня три вопроса.

  1. Может кто-нибудь сказать мне, если это хороший способ сделать это?
  2. Любая идея, почему моя переменная _finYear иногда опускается, что приводит к ошибке.
  3. Можно ли вернуть данные из задачи?

Спасибо

Mike

1 Ответ

0 голосов
/ 11 августа 2011
  1. Нет ничего принципиально неправильного в этом подходе.
  2. Вы не показываете, откуда _finYear берется в вашем коде, но основываясь на коде, который вы показываете, я не вижу причин, по которым он не будет правильно передан sproc.
  3. Конечно, вы можете вернуть DataTable, как это. Не безопасно получать доступ одновременно из нескольких потоков, но это может быть передано через потоки без проблем.

Единственная незначительная ошибка в вашем коде заключается в том, что у вас должна быть еще одна попытка / окончание в логике обработки продолжения, поскольку t.Result может выдать исключение, если возникла проблема с асинхронным вызовом Begin/EndExecuteReader, и это оставил бы вас не избавляясь от команды и соединения. Так что это было бы лучше:

readerTask.ContinueWith(t =>            
{                
    try
    {
        var reader = t.Result;                

        try                
        {                    
            reader.Read();
            data.Load(reader);

            return data;
        }
        finally
        {                    
            reader.Dispose();
        }            
    }
    finally
    {
        command.Connection.Close();
        command.Connection.Dispose();
        command.Dispose();                
    }
});
...