Параллельный.Запрос Foreach SQL иногда приводит к подключению
Мне нужно ускорить выполнение 12 запросов в моем приложении. Я переключился с обычного предисловия на параллельное.инструкция foreach. Но иногда я получаю сообщение об ошибке: "ExecuteReader требует открытого и доступного соединения."Насколько я понимаю, поскольку многие из 12 запросов используют один и тот же InitialCatalog, на самом деле нет нового соединения для 12, и это может быть проблемой? Как я могу это исправить? "sql" - это список типа " Sql " - a класс - это просто имя строки, строка connectiona и список запросов. Вот код:
/// <summary>
/// Connects to SQL, performs all queries and stores results in a list of DataTables
/// </summary>
/// <returns>List of data tables for each query in the config file</returns>
public List<DataTable> GetAllData()
{
Stopwatch sw = new Stopwatch();
sw.Start();
List<DataTable> data = new List<DataTable>();
List<Sql> sql=new List<Sql>();
Sql one = new Sql();
one.connection = "Data Source=XXX-SQL1;Initial Catalog=XXXDB;Integrated Security=True";
one.name = "Col1";
one.queries.Add("SELECT Name FROM [Reports]");
one.queries.Add("SELECT Other FROM [Reports2]");
sql.Add(one);
Sql two = new Sql();
two.connection = "Data Source=XXX-SQL1;Initial Catalog=XXXDB;Integrated Security=True";
two.name = "Col2";
two.queries.Add("SELECT AlternateName FROM [Reports1]");
sql.Add(two);
Sql three = new Sql();
three.connection = "Data Source=YYY-SQL2;Initial Catalog=YYYDB;Integrated Security=True";
three.name = "Col3";
three.queries.Add("SELECT Frequency FROM Times");
sql.Add(three);
try
{
// ParallelOptions options = new ParallelOptions();
//options.MaxDegreeOfParallelism = 3;
// Parallel.ForEach(sql, options, s =>
Parallel.ForEach(sql, s =>
//foreach (Sql s in sql)
{
foreach (string q in s.queries)
{
using (connection = new SqlConnection(s.connection))
{
connection.Open();
DataTable dt = new DataTable();
dt.TableName = s.name;
command = new SqlCommand(q, connection);
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
adapter.Fill(dt);
//adapter.Dispose();
lock (data)
{
data.Add(dt);
}
}
}
}
);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GetAllData error");
}
sw.Stop();
MessageBox.Show(sw.Elapsed.ToString());
return data;
}
Вот класс Sql, который я сделал, который вам понадобится:
/// <summary>
/// Class defines a SQL connection and its respective queries
/// </summary>
public class Sql
{
/// <summary>
/// Name of the connection/query
/// </summary>
public string name { get; set; }
/// <summary>
/// SQL Connection string
/// </summary>
public string connection { get; set; }
/// <summary>
/// List of SQL queries for a connection
/// </summary>
public List<string> queries = new List<string>();
}
3 ответа:
Я бы рефакторировал вашу бизнес-логику (подключение к базе данных).
public class SqlOperation { public SqlOperation() { Queries = new List<string>(); } public string TableName { get; set; } public string ConnectionString { get; set; } public List<string> Queries { get; set; } } public static List<DataTable> GetAllData(IEnumerable<SqlOperation> sql) { var taskArray = sql.SelectMany(s => s.Queries .Select(query => Task.Run(() => //Task.Factory.StartNew for .NET 4.0 ExecuteQuery(s.ConnectionString, s.TableName, query)))) .ToArray(); try { Task.WaitAll(taskArray); } catch(AggregateException e) { MessageBox.Show(e.ToString(), "GetAllData error"); } return taskArray.Where(t => !t.IsFaulted).Select(t => t.Result).ToList(); } public static DataTable ExecuteQuery(string connectionString, string tableName, string query) { DataTable dataTable = null; using (var connection = new SqlConnection(connectionString)) { dataTable = new DataTable(); dataTable.TableName = tableName; using(var command = new SqlCommand(query, connection)) { connection.Open(); using(var adapter = new SqlDataAdapter()) { adapter.SelectCommand = command; adapter.Fill(dataTable); } } } return dataTable; }
Ado.Net имеет довольно умный пул соединений, поэтому в целом вы должны просто открывать соединения и закрывать соединения по команде, и пусть пул обрабатывает, действительно ли они открываются или закрываются.
Итак, одно соединение на команду:
Parallel.ForEach(sql, s=> //foreach (Sql s in sql) { foreach (string q in s.queries) { using (connection = new SqlConnection(s.connection)) { connection.Open(); DataTable dt = new DataTable(); dt.TableName = s.name; command = new SqlCommand(q, connection); SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = command; adapter.Fill(dt); //adapter.Dispose(); lock(data){ data.Add(dt); } } } }