Выполнение хранимых процедур параллельно
у меня есть эти 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());
но на последней строке вылетает с
есть одна или несколько ошибок.`
внутреннее исключение составляет
ссылка на объект не указывает на экземпляр объекта.
стек Трейс!--8-->
в систему.Нарезка резьбы.Задачи.Задача.WaitAll (Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
на connectionString
скачать с System.Data.Entity.DbContext.Database
класс
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
тип возврата, но я не требуется дублировать эти методы.
2 ответов
на database.Connection.ConnectionString
is статическая строка, иначе вы не можете скомпилировать due к "ссылка на объект требуется для нестатического поля, метода или свойства".
имея это в виду, это не строка соединения, которая не является интстатированной, потому что ее статическая... и даже если вы намеренно инициализировали статическую строку в 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 будет брошен именно в том месте, где он происходит, тогда вы наведете курсор мыши на переменные в строке кода и увидите, какой объект равен null.
обновление:
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;
}
}