【发布时间】:2013-06-10 21:16:58
【问题描述】:
我在 SQL Server 数据库中有一个表,它表示从正在运行的 Windows 服务插入的一些操作的日志文件。一切正常。
但是,我有一个 Windows 应用程序,它获取已插入日志表的最新行并在 DataGridView 中查看它。在开发这个应用程序时,我依赖于来自 MSDN 的 Using SqlDependency in a Windows Application。它运行良好,但是当日志表接收到大量日志详细信息时,Windows 应用程序挂起,主线程池变得太忙。
我想通过使用Thread 类或BackgroundWorker 控件在单独的线程池中运行上一个链接中引用的相同代码。这意味着一个线程用于使用 UI 控件,另一个线程用于侦听数据库更改并将其放入DataGridView。
您可以从此链接查看 UI 截图 "UI"
没有。 (1):这个 GroupBox 代表了用户在监控时可以使用的 UI 工具。
没有。 (2):开始按钮负责开始监听和接收来自数据库的更新并重新填充DataGridView。
没有。 (3):这个网格代表已经插入数据库的新日志。
没有。 (4):这个数字(38个变化)表示监听sql依赖对数据库变化的计数。
我的代码: 公共部分类 frmMain :表格 { SqlConnection 连接;
const string tableName = "OutgoingLog";
const string statusMessage = "{0} changes have occurred.";
int changeCount = 0;
private static DataSet dataToWatch = null;
private static SqlConnection connection = null;
private static SqlCommand command = null;
public frmMain()
{
InitializeComponent();
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// Updating the UI from a worker thread is not permitted.
// The following code checks to see if it is safe to
// update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code
// is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch.
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);
object[] args = { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler, since it is only good
// for a single notification.
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
// At this point, the code is executing on the
// UI thread, so it is safe to update the UI.
++changeCount;
lblChanges.Text = String.Format(statusMessage, changeCount);
this.Refresh();
// Reload the dataset that is bound to the grid.
GetData();
}
private void GetData()
{
// Empty the dataset so that there is only
// one batch of data displayed.
dataToWatch.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataToWatch, tableName);
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
}
}
private void btnStart_Click(object sender, EventArgs e)
{
changeCount = 0;
lblChanges.Text = String.Format(statusMessage, changeCount);
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop("<my connection string>");
SqlDependency.Start("<my connection string>");
if (connection == null)
{
connection = new SqlConnection("<my connection string>");
}
if (command == null)
{
command = new SqlCommand("select * from OutgoingLog", connection);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private void frmMain_Load(object sender, EventArgs e)
{
btnStart.Enabled = CanRequestNotifications();
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
SqlDependency.Stop("<my connection string>");
}
}
我到底想要什么:当用户点击开始按钮时,应用程序在单独的线程池中运行代码。
【问题讨论】:
-
将依赖项与例如 Backgroundworker 混合会使事情复杂化。也许考虑只使用一个。
-
这个应用程序的有趣练习将是一个应用程序监听器订阅 win 服务和服务发送日志表更新的信号。届时win app会去获取新数据。
-
感谢您的支持,但我不能像您说的那样将应用程序分成两部分。我必须根据前面的解释来开发它。我只需要将相同的代码运行到一个单独的 线程池 或使用 BackgroundWorker 运行它。
标签: c# multithreading backgroundworker sqldependency