C#在单独的线程中连接数据库

6
我有一个应用程序,它坐落在任务栏中并定期检查数据库(通过SQLConnection)。如果无法连接到数据库,它只会在通知图标上显示红色状态,什么也不做。这是有意的,因为用户将在设施之间旅行,数据库只能在内部网络上或通过VPN连接时才能访问。

问题是当数据库无法连接时,我仍希望通知图标上下文菜单能够响应。目前,如果用户在数据库正在尝试连接时尝试与菜单交互,线程将被连接尝试锁定。

我想要的是在单独的线程中进行数据库连接,但我知道在线程之间共享连接对象是不好的。一旦数据库连接成功,我需要从原始线程查询它。有没有好的方法可以做到这一点?

我知道我可以有一个函数在新线程中运行,并简单地检查连接是否失败,然后如果返回成功,原始线程就可以继续并连接自己的数据库对象。但这种方法似乎是一个不好的解决方法。有什么好的想法吗?

谢谢!


感谢您的建议-我从中学到了很多。最终,我做了我应该从一开始就做的事情。主(UI)线程只需在需要定期数据库更新时启动一个新线程(其中包括数据库对象的创建和连接)。

因此,就像HenkHolterman在我的原始消息中建议的那样,我实际上不需要在单独的线程中创建数据库并运行操作。

一旦我升级到VS2010,我一定会有兴趣使用SqlConnection.OpenAsync。

再次感谢。


为什么需要在原始线程中进行查询?这可能会再次阻塞。 - H H
3个回答

7

SqlConnection没有线程关联要求,因此您可以在单独的线程上创建并打开它,并将引用传回UI线程。只要不同时从多个线程调用同一实例的任何属性或方法,您就不应该遇到任何问题。

使用Task类(System.Threading.Task)在后台开始此操作,然后调用ContinueWith将所有权交回UI线程。

public class TrayIconForm : Form
{
  private SqlConnection connection = null;

  private void Form_Load(object sender, EventArgs args)
  {
    TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();

    Task.Factory.StartNew(() =>
      {
        var sql = new SqlConnection();
        sql.ConnectionString = "your connection string";
        sql.Open();
        return sql;
      }).ContinueWith((task =>
      {
        connection = task.Result;
        // You can access other UI elements here as needed.
      }), ui);
  }
}

通过在C# 5.0中引入的新的asyncawait关键字(现在可以通过Async CTP获取),您可以做到这一点。

private async void Form_Load(object sender, EventArgs args)
{
  connection = await Task.Run(() =>   // TaskEx.Run in the CTP
    {
      var sql = new SqlConnection();
      sql.ConnectionString = "your connection string";
      sql.Open();
      return sql;
    });
}

2

有点冒险?当.Open()阻塞多秒时,这并不是一个真正的选择。 - H H
看起来是一个不错的解决方案,但我还没有决定经历繁琐的公司流程来升级到VS2010 - 目前仍在使用VS2008。谢谢你的建议! - user1259576
@HenkHolterman 我猜你已经尝试过了。我在F#中使用这种编程模型的经验非常好,因此强烈推荐在C#中也使用它。 - GregC
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.openasync%28v=VS.110%29.aspx - GregC
+1:这绝对是我今后处理事情的方式。在我看来,await关键字使得这变得非常简单和优雅。 - Brian Gideon
显示剩余3条评论

1

使用BackgroundWorker对象可以简单地解决您的问题,但更准确的做法是使用线程来尝试打开连接,使UI线程准备接收用户输入。


一旦后台线程打开了连接,我该如何从UI线程与其交互?我需要在UI线程中创建连接对象,将其传递给连接线程,并在连接线程完成后继续吗? - user1259576
这完全取决于您对该连接需要做什么。在处理线程安全时要非常小心。如果您使用的是在两个线程之间共享作用域的对象,请在使用时小心锁定该对象。 - CodeMonkey1313
1
你可以简单地将已打开的连接传递给“Completed”事件。 - H H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接