Parallel.Foreach query SQL a volte risultati in connessione

Devo accelerare l’esecuzione di 12 query nella mia applicazione. Sono passato da un normale foreach a Parallel.ForEach. Ma a volte viene visualizzato un errore che dice “ExecuteReader richiede una connessione aperta e disponibile. Lo stato corrente della connessione è in connessione.” Mi risulta che, dal momento che molte delle 12 query utilizzano lo stesso InitialCatalog, non esiste una nuova connessione per il 12 e questo potrebbe essere il problema? Come posso risolvere questo? “sql” è un elenco di tipo “Sql”: una class è solo un nome di stringa, una stringa di connessione e un elenco di query. Ecco il codice:

///  /// Connects to SQL, performs all queries and stores results in a list of DataTables ///  /// List of data tables for each query in the config file public List GetAllData() { Stopwatch sw = new Stopwatch(); sw.Start(); List data = new List(); List sql=new List(); 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; } 

Ecco la class Sql che ho creato che avresti bisogno di:

 ///  /// Class defines a SQL connection and its respective queries ///  public class Sql { ///  /// Name of the connection/query ///  public string name { get; set; } ///  /// SQL Connection string ///  public string connection { get; set; } ///  /// List of SQL queries for a connection ///  public List queries = new List(); } 

Vorrei rifattorizzare la tua logica aziendale (connettendomi al database).

 public class SqlOperation { public SqlOperation() { Queries = new List(); } public string TableName { get; set; } public string ConnectionString { get; set; } public List Queries { get; set; } } public static List GetAllData(IEnumerable 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 ha un pool di connessioni piuttosto intelligente, quindi in generale è sufficiente aprire le connessioni e chiudere le connessioni per comando e lasciare che il pool gestisca se sono realmente aperte o chiuse.

Quindi una connessione per comando:

  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); } } } } 

Puoi anche usare MultipleActiveResultSets = true; nella stringa di connessione per supportare più lettori