连接到不同线程上的数据库

时间:2012-07-25 21:07:57

标签: c# wpf multithreading sqlconnection

我有一个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 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

来自DBConnection's documentation

  

此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。 不保证所有实例成员都是线程安全的。

换句话说,不同的线程永远不应共享数据库连接,因为无法安全地共享实例。正如Ted Spence建议的那样,您应该只在您需要时创建连接(并在完成它们时.Dispose()。 .NET有一个内置的连接池机制,可以很好地确保连接在可能的情况下被重用,但是保持连接的时间超过了绝对必要的时间会干扰它的能力。

答案 2 :(得分:0)

尝试

dbtool tool = e.result as dbtool;

如果dbTool中的变量在查询完成时设置为true或false,那么您应该可以调用

tool.variable = true/false

答案 3 :(得分:0)

感谢大家的意见。

我能够想出一个解决方案。在遇到Working With The WPF Dispatcher之后。我确定你可以获得UI线程的Dispatcher对象:

//...

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

这确实会测试不同线程上的连接字符串,使应用程序的其余部分响应。