5

ユーザーがいくつかのテキストボックスにデータベース情報を入力する WPF アプリケーションがあります。ユーザーが「接続」をクリックすると、ユーザーが入力した内容から接続文字列が作成され、接続が確立されます。ユーザーが間違った情報を入力すると、接続がタイムアウトするまでアプリケーションがハングアップすることに気付きました。ハングとは、ユーザーがアプリケーションの残りの部分をまったく操作できないことを意味します。

接続文字列がテストされている間、アプリケーションの応答性を維持することが私の目標です。

このワークフローを別のスレッドに置くことは良い解決策だと思いました。私の考えは、スレッドの実行中にデータベース接続が必要になる可能性のあるものをすべて無効にすることです。スレッドが戻ってきたら (接続文字列が有効であることを確認したら)、すべてを再度有効にします。それ以外の場合は、すべてを無効のままにします。

ただし、Threadクラスには、スレッドが完了したときにイベント通知がありません (または、少なくとも私は気づいていません)。

私もBackgroundWorkerクラスで取り組みました。これはよりうまく機能します。ただし、RunWorkerCompletedEventHandlerイベントが発生し、接続文字列が有効でない場合、次の例外が発生します。

別のスレッドがこのオブジェクトを所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。

これは、完了したイベント ハンドラーが起動されたときに接続がまだタイムアウトしていないことが原因であると考えられます。

誰にもアイデアはありますか、またはデータベースへの接続をマルチスレッド化しようとしないでください。

私がやっていることのコードの概要:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    dbTool = new DBTool();
    // Create the connection string
    e.Result = dbTool.connectToDB(); // connectToDB() returns a bool (true if connection established)
}

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // connectToDB() returns a bool (true if connection established)
    if(e.Result == true)  // Trying to read e.Result here throws the exception
    {
        // e.Error and e.Cancel should be checked first
        // However, I would like the thread to finish before
        // this event is fired
    }
    if (e.Error != null)
    {
        Console.WriteLine(e.Error.Message);
    }
}
4

4 に答える 4

3

DbConnectionオブジェクトを単一のグローバル変数に保存してスレッド間で共有しないでください。

.NET 環境は自動的に接続をプールして共有するため、呼び出しnew DbConnection()は非常に高速です。

接続文字列をグローバル変数に保持する必要がありますが、各スレッドで必要に応じて接続を作成します。

編集: 元の投稿者は、接続文字列のテスト中に WinForms アプリケーションの応答性を維持する方法について実際にアイデアを求めていた可能性があります。その場合、別のスレッドを生成して接続をテストする必要があります。「接続テスト スレッド」から、次のパターンに従って UI を更新できます - How to update the GUI from another thread in C#?

public void TestConnectionThread(String connstr_to_test)
{
    // Notify the user that we're doing our test
    string message = "Testing...";
    lblTestResultMessage.SetPropertyThreadSafe(() => lblTestResultMessage.Text, message);

    try {
        dbTool = new DBTool();
        message = dbTool.connectToDB();

    // If something failed, show a useful debugging message
    } catch (Exception ex) {
        message = ex.ToString();
    }

    // Use a lambda expression to communicate results to the user safely
    lblTestResultMessage.SetPropertyThreadSafe(() => lblTestResultMessage.Text, message);
}
于 2012-07-25T21:10:07.310 に答える
0

Thank you everyone for your input.

I was able to come up with a solution. After coming across Working With The WPF Dispatcher. I determined that you can obtain the UI thread's Dispatcher object:

//...

dbTool = new DBTool();
// Initialize the connection string
// Disable some UI
Thread thread = new Thread(new ThreadStart(
        delegate()
        {
            dbTool.connectToDB();
            UIControl.Dispatcher.BeginInvoke(
              new Action(
                  update
            ));
        }
));
thread.Start();

//.....

void update()
{
    if (dbTool.validString)     // If the connection string was valid
    {
        // Re-enable controls
    }
    else     // Invalid connection string
    {
        // Keep controls disabled if no connection could be created
    }
}

This indeed will test the connection string on a different thread, leaving the rest of the application responsive.

于 2012-07-26T19:13:54.100 に答える
0

DBConnection のドキュメントから:

この型の public static (Visual Basic では共有) メンバーはすべて、スレッド セーフです。インスタンス メンバーは、スレッド セーフであるとは限りません。

つまり、インスタンスを安全に共有できないため、異なるスレッドがデータベース接続を共有することはありません。Ted Spence が示唆しているように、代わりに、必要な.Dispose()ときにのみ接続を作成する必要があります (接続が完了したら接続を作成します)。.NET には組み込みの接続プール メカニズムがあり、可能な場合は接続が再利用されるようにしますが、絶対に必要な期間を超えて接続を保持すると、その機能が妨げられる可能性があります。

于 2012-07-25T21:15:27.357 に答える
0

試す

dbtool tool = e.result as dbtool;

クエリの完了時に true または false に設定される変数が dbTool にある場合は、呼び出すことができるはずです。

tool.variable = true/false
于 2012-07-25T21:28:00.643 に答える