C# 如何异步查询数据库
以前在学习Socket的时候,经常会使用到异步操作,孰不知将异步用到数据库查询上也是一把利器,特别是在大数据量查询的时候效果应该是比较明显的。我顺便写了一个小例子,供大家参考。
我们平时默认使用的查询是同步的,也就是说一方不等待另一方做好准备,当查询时间过长时,客户端会被一直阻塞在这里而不能做其他事情。而当我们使用异步时,程序并不会阻塞或挂起线程,它会通过一个代理的回调方法完成查询,主线程将会继续执行,这样便解决了同步所不能做到的并行执行的结果。
代码如下所示:
1 public partial class AsyncSearchForm : Form
2 {
3 #region --Delegates--
4 private delegate void DisplayTimeInfoDelegate(string text);
5 private delegate void DisplayDataGridViewDelegate(DataTable myTable);
6 private delegate void DisplayInfoDelegate(string text);
7 #endregion
8
9 #region --Fields--
10 private SqlConnection mConnection;
11 private Stopwatch mWatch;
12 private DisplayTimeInfoDelegate myTimeDelegate;
13 private DisplayDataGridViewDelegate myDataDelegate;
14 #endregion
15
16 #region --Constructor--
17 public AsyncSearchForm()
18 {
19 InitializeComponent();
20 mWatch = new Stopwatch();
21 myDataDelegate = new DisplayDataGridViewDelegate(DisplayDataResults);
22 myTimeDelegate = new DisplayTimeInfoDelegate(DisplayTimeResults);
23 button_DoSearch.Click += new EventHandler(Button_DoSearch_Click);
24 }
25 #endregion
26
27 #region --Event Methods--
28 /// <summary>
29 /// 当点击执行查询时发生
30 /// </summary>
31 private void Button_DoSearch_Click(object sender, EventArgs e)
32 {
33 Application.DoEvents();
34 string connectionString = @"Data Source=127.0.0.1;Integrated Security=SSPI;Database=Northwind;Asynchronous Processing=true";
35 mConnection = new SqlConnection(connectionString);
36 string sqlString = "select * from Orders";
37 SqlCommand command = new SqlCommand(sqlString, mConnection);
38 mConnection.Open();
39 mWatch.Start();
40 AsyncCallback callBack = new AsyncCallback(HandleCallback);//注册回调方法
41 //开始执行异步查询,将Command作为参数传递到回调函数以便执行End操作
42 command.BeginExecuteReader(callBack, command);
43 }
44 #endregion
45
46 #region --Private Methods--
47 /// <summary>
48 /// 异步查询的回调方法
49 /// </summary>
50 /// <param name="MyResult">异步操作状态</param>
51 private void HandleCallback(IAsyncResult MyResult)
52 {
53 try
54 {
55 SqlCommand command = (SqlCommand)MyResult.AsyncState;
56 SqlDataReader reader = command.EndExecuteReader(MyResult);
57 mWatch.Stop();
58 string callBackTime = mWatch.ElapsedMilliseconds.ToString() + "毫秒";
59 DataTable dataTable = new DataTable();
60 dataTable.Load(reader);
61 this.Invoke(myTimeDelegate, callBackTime);
62 this.Invoke(myDataDelegate, dataTable);
63 }
64 catch (Exception MyEx)
65 {
66 this.Invoke(new DisplayInfoDelegate(DisplayTimeResults), String.Format(MyEx.Message));
67 }
68 finally
69 {
70 if (mConnection != null)
71 {
72 mConnection.Close();
73 }
74 }
75 }
76
77 /// <summary>
78 /// 在Label上显示信息
79 /// </summary>
80 /// <param name="text"></param>
81 private void DisplayTimeResults(string text)
82 {
83 this.label_Time.Text = label_Time.Text + text;
84 }
85
86 /// <summary>
87 /// 绑定数据到DataGridView
88 /// </summary>
89 /// <param name="myTable"></param>
90 private void DisplayDataResults(DataTable myTable)
91 {
92 this.dgv_Data.DataSource = myTable;
93 }
94 #endregion
95 }
运行结果如下所示:
代码很简单,没有很多的东西。数据库我使用了Northwind,这次查询用了4毫秒,在这样短的查询耗时下,异步对程序作用是不明显的,真正使用时还需具体问题具体分析。需要提醒大家的是异步操作是普遍存在的,但异步方式也需要某种程度的协调,异步看似效率高无阻塞,但若程序在还没得到查询结果的时候就调用了使用查询结果的方法就会出错。因此还可以使用ManualResetEvent手动给线程指定阻塞和非阻塞的信号,这样各个线程之间就可以等待彼此的信号达到同步目的,有兴趣的朋友可以试试,我在这就不再赘述了。最后送大家一句话:“异步有风险,使用需谨慎。”