Выполнять хранимые процедуры параллельно

У меня есть эти 2 метода

public DataTable GetData1(int Id)
{
    DataTable dt = new DataTable();

    using (SqlConnection sqlcon = new SqlConnection(database.Connection.ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand("spGetData1", sqlcon))
        {
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id});

            using (SqlDataAdapter da = new SqlDataAdapter(cmd))
            {
                da.Fill(dt);
            }
        }
    }

    return dt;
}

public DataTable GetData2(int Id)
{
    DataTable dt = new DataTable();

    using (SqlConnection sqlcon = new SqlConnection(database.Connection.ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand("spGetData2", sqlcon))
        {
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id});

            using (SqlDataAdapter da = new SqlDataAdapter(cmd))
            {
                da.Fill(dt);
            }
        }
    }
    return dt;
}

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

Я пробовал что-то вроде

var task1 = Task.Factory.StartNew(() => database.Data.GetData1(1));
var task2 = Task.Factory.StartNew(() => database.Data.GetData2(2));

var taskList = new List<Task> { task1, task2 };

Task.WaitAll(taskList.ToArray());

но в последней строке он сбрасывается с помощью

существует одна или несколько ошибок.

Внутреннее исключение

Ссылка на объект не установлена ​​в экземпляр объекта.

Трассировка стека

в System.Threading.Tasks.Task.WaitAll(Задача [] задачи, Int32 миллисекундыTimeout, CancellationToken cancelationToken)

connectionString получается из System.Data.Entity.DbContext.Database class

 public class DatabaseRepository : IDisposable
    {
        DbContext dbContext;

        public DatabaseRepository()
        {
             dbContext = new DbContext("connection string ...");
             Data = new DataRepository(dbContext.Database);

        }
        public DataRepository Data { get; set; }
}

введите описание изображения здесь

но ошибка такая же, даже я установил строку соединения вручную, поэтому я не думаю, что ошибка здесь.

   using (SqlConnection sqlcon = new SqlConnection("connection string ..."))
   {
      using (SqlCommand cmd = new SqlCommand("spGetData2", sqlcon))
      {
       ...
      }
   }

Как я могу это сделать? Я вижу, что в некоторых примерах используется тип возвращаемого типа Async, но я не хочу дублировать эти методы.

Ответ 1

database.Connection.ConnectionString представляет собой статическую строку, иначе вы не можете скомпилировать значение "Ссылка на объект требуется для нестатического поля, метода или свойства".

Имея это в виду, это не строка соединения, которая не intstatiated, потому что ее статический... и даже если вы намеренно инициализировали статическую строку Null, тогда сообщение об ошибке будет:

InnerException = { "Свойство ConnectionString не было инициализировано." }

Вот репродукция, и ошибка не может быть произведена, если ваши методы GetData в пустых объектах:

namespace database
{
public class Program
{
    static void Main(string[] args)
    {
        //WORKS!!
        var repro = new database.Data();
        var task1 = Task.Factory.StartNew(() => repro.GetData1(3));
        var task2 = Task.Factory.StartNew(() => repro.GetData2(5));
        var taskList = new List<Task> { task1, task2 };
        Task.WaitAll(taskList.ToArray());

        //FAILS WITH ERROR REPORTED!!
        repro = null;
        var task1 = Task.Factory.StartNew(() => repro.GetData1(3));
        var task2 = Task.Factory.StartNew(() => repro.GetData2(5));
        var taskList = new List<Task> { task1, task2 };
        Task.WaitAll(taskList.ToArray());
    }
}

class Data
{
    private string connectionString = "Server=.;Database=CRUD_Sample;Integrated Security=True;Asynchronous Processing = True;";
    public DataTable GetData1(int Id)
    {
        DataTable dt = new DataTable();
        using (SqlConnection sqlcon = new SqlConnection(connectionString))
        {
            using (SqlCommand cmd = new SqlCommand("Get_CustomerbyID", sqlcon))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id });
                using (SqlDataAdapter da = new SqlDataAdapter(cmd))
                {
                    da.Fill(dt);
                }
            }
        }
        return dt;
    }

    public DataTable GetData2(int Id)
    {
        DataTable dt = new DataTable();
        using (SqlConnection sqlcon = new SqlConnection(connectionString))
        {
            using (SqlCommand cmd = new SqlCommand("Get_CustomerbyID", sqlcon))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id });
                using (SqlDataAdapter da = new SqlDataAdapter(cmd))
                {
                    da.Fill(dt);
                }
            }
        }
        return dt;
    }
}
}

Отладка

Как вы находите источник исключения NullReferenceException? Помимо рассмотрения самого исключения - ключ NRE будет выбрасываться точно в том месте, где оно происходит, тогда вы наведите указатель мыши на переменные в строке кода и посмотрите, какой объект является нулевым.

Ответ 2

UPDATE:

Task.WaitAll вызывает блокировку текущего потока, пока все не завершится. Используйте Task.WhenAll, чтобы не связывать другие потоки, ожидая завершения задач.

var task1 = Task.Factory.StartNew(() => database.Data.GetData1(1));
var task2 = Task.Factory.StartNew(() => database.Data.GetData2(2));

var taskList = new List<Task> { task1, task2 };

await Task.WhenAll(taskList.ToArray());

var result1 = await task1;
var result2 = await task2;

Оригинальный ответ. (Все еще применимо)

Основываясь на дополнительной информации, представленной в комментарии, я делаю предположения о классе, инкапсулирующем данный код. Возможно, что database.Connection выходит за пределы области действия, когда выполняется параллельно, что может вызвать NRE. Извлеките строку соединения ранее в жизненном цикле объекта и повторно используйте его при получении данных.

 public class MyDataClass {

    string connectionString;
    private Database database;

    public MyDataClass(DbContext context) {
        this.database = context.Database;
        connectionString = database.Connection.ConnectionString;
    }

    public DataTable GetData1(int Id) {
        var dt = new DataTable();
        using (var sqlcon = new SqlConnection(connectionString)) {
            using (var cmd = new SqlCommand("spGetData1", sqlcon)) {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id });

                using (var da = new SqlDataAdapter(cmd)) {
                    da.Fill(dt);
                }
            }
        }

        return dt;
    }

    public DataTable GetData2(int Id) {
        var dt = new DataTable();
        using (var sqlcon = new SqlConnection(connectionString)) {
            using (var cmd = new SqlCommand("spGetData2", sqlcon)) {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id });

                using (var da = new SqlDataAdapter(cmd)) {
                    da.Fill(dt);
                }
            }
        }
        return dt;
    }
}