2

何十もの記事を調べていましたが、解決策が見つかりませんでした。

ロジックは次のとおりです。

Winform(VS2010)アプリケーションがあり、SQL Server 2008 R2 Expressテーブルからデータを読み取りA、いくつかの計算を処理して、別のテーブルに格納する必要がありますB

実行時間を短縮するために並列を使用したいForEach(そうでない場合、計算+ SQLプロセスには数日かかります.....)

データベースには500万行を超える行があるため、SQLから読み取る必要があります。読み取るたびに、数百行が返されます。

リストは次のように定義されます:

BindingList<ItemsClass> etqM = new BindingList<ItemsClass>();
BindingList<ItemsClass> etqC = new BindingList<ItemsClass>();

並列実行:

Parallel.ForEach(etqC, cv => {
            readData(ref etqM, "tableA", "WHERE ID LIKE '" + cv.Name + "%'");
            IList<ItemsClass> eResults = etqM.OrderBy(f => f.ID).ToList();

            foreach (ItemsClass R in eResults)
            {
                //calculations comes here

                etqM[rID] = R;
            }

            Parallel.ForEach(etqM, r => {
                // part 2 of calculations comes here
                }
            });
            exportList(etqM, "tableB", true);
        });

SQL読み取り関数:この関数は、SQLレコードから読み取られたSQLのリスト、テーブル名、および条件を取得し、それらをリスト形式に変換します。

public void readData<T>(ref BindingList<T> etqList, string tableName, string conditions)
    {
        SqlConnection myConnection = new SqlConnection();
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandTimeout = 0;
        myCommand.Connection = myConnection;

        etqList.Clear();            
        openConn(myConnection);
        SqlDataReader myReader = null;

        try
        {
            int totalResults;
            myCommand.CommandText = "SELECT COUNT (*) FROM " + tableName + " " + conditions;
            totalResults = (int)myCommand.ExecuteScalar();

            if (totalResults > 0)
            {
                myCommand.CommandText = "SELECT * FROM " + tableName + " " + conditions;
                myReader = myCommand.ExecuteReader();
                etqList = ConvertTo<T>(convertReaderToDT(myReader));
            }
        }
        catch (Exception ex) { }
        finally
        {
            try { myReader.Close(); }
            catch { }
        }
        closeConn(myConnection);
    }

SQLエクスポート関数:この関数は、指定されたリストをテーブル名にエクスポートします。

private void exportListToSql<T>(IEnumerable<T> etqList, string tableName)
{
        SqlConnection myConnection = new SqlConnection();
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandTimeout = 0;
        myCommand.Connection = myConnection;
        openConn(myConnection);

        try
        {
            actionTotalCount++;
            DataTable dt = new DataTable(tableName);
            dt = ToDataTable(etqList);//List Name
            var bulkCopy = new SqlBulkCopy(myConnection,
                SqlBulkCopyOptions.TableLock |
                SqlBulkCopyOptions.FireTriggers |
                SqlBulkCopyOptions.UseInternalTransaction,
                null
                );
            bulkCopy.DestinationTableName = tableName;
            bulkCopy.BatchSize = BATCH_SIZE;
            bulkCopy.WriteToServer(dt);
        }
        catch (Exception ex) {  }
        finally { closeConn(myConnection); }
    }

SQLopenConncloseConn

void openConn(SqlConnection myConnection)
{
        if (myConnection.State == ConnectionState.Open) return;
        myConnection.ConnectionString = "Data Source=" + DB_NAME + ";Initial Catalog=APPDB;Integrated Security=True;Connect Timeout=120;Asynchronous Processing=true;";

        try { myConnection.Open(); actionTotalCount++; }
        catch (System.Exception ex) { MessageBox.Show(ex.Message); }
}

void closeConn(SqlConnection myConnection)
{
        if (myConnection.State == ConnectionState.Fetching || myConnection.State == ConnectionState.Executing || myConnection.State == ConnectionState.Connecting) return;
        try { myConnection.Dispose(); }
        catch (System.Exception ex) { MessageBox.Show(ex.Message); }
}

問題は次のとおりです。実行すると、次のメッセージが表示されます。

ExecuteScalarには、オープンで利用可能な接続が必要です。接続の現在の状態は閉じられています。

このメッセージは、最初のスレッドを除くすべてのスレッドに届きます。

何か案は ?

4

1 に答える 1

0

どうやら問題は SQL ではなく、計算にあったようです。List は「Parallel.Foreach」の外部で定義されているため、別のスレッドから同時にアクセスされ、クラッシュが発生していました。

次のようにコードを変更すると、すべてうまくいきました:

Parallel.ForEach(etqC, cv => {

        BindingList<ItemsClass> etqM = new BindingList<ItemsClass>();

        readData(ref etqM, "tableA", "WHERE ID LIKE '" + cv.Name + "%'");
        IList<ItemsClass> eResults = etqM.OrderBy(f => f.ID).ToList();

        foreach (ItemsClass R in eResults)
        {
            //calculations comes here

            etqM[rID] = R;
        }

        Parallel.ForEach(etqM, r => {
            // part 2 of calculations comes here
            }
        });
        exportList(etqM, "tableB", true);
    });
于 2012-10-25T14:36:01.390 に答える