5

这是我第一次需要使用 SqlDependency,所以我希望这是我犯的一个愚蠢的错误。

我遇到的问题是当 sql 表更改时 OnChanged 事件不会触发。没有错误或任何东西,它不会触发。

这是代码

public class SqlWatcher
{
    private const string SqlConnectionString = "Data Source = CN-PC08\\DEV; Initial Catalog=DEP; User = sa; Password=******";

    public SqlWatcher()
    {
        SqlClientPermission perm = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);
        perm.Demand();

        SqlCommand cmd = new SqlCommand("SELECT [DataAvaliable], [RowNumber] FROM [dbo].[Trigger]", new SqlConnection(SqlConnectionString));
        SqlDependency sqlDependency = new SqlDependency(cmd);
        sqlDependency.OnChange += On_SqlBitChanged;
    }

    private void On_SqlBitChanged(object sender, SqlNotificationEventArgs sqlNotificationEventArgs)
    {
        SqlDependency dependency = (SqlDependency)sender;
        dependency.OnChange -= On_SqlBitChanged;

        // Fire the event
        if (NewMessage != null)
        {
            NewMessage(this, new EventArgs());
        }
    }

    public void Start()
    {
        SqlDependency.Start(SqlConnectionString);
    }

    public void Stop()
    {
        SqlDependency.Stop(SqlConnectionString);
    }

    public event EventHandler NewMessage;

在我的主窗口中,我有这个

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        try
        {
            SqlWatcher sqlWatcher = new SqlWatcher();
            sqlWatcher.Start();
            sqlWatcher.NewMessage += On_NewMessage;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

    private void On_NewMessage(object sender, EventArgs eventArgs)
    {
        MessageBox.Show("Message Received");
    }
}

所以预期的行为是,如果我运行以下 sqlQuery,将显示一个 messageBox,说“收到消息”

INSERT INTO [DEP].[dbo].[Trigger] Values(0,3)

谁能给我一个关于检查/更改什么的提示?

我知道只能在依赖项中使用 Sql 功能的一个子集,但我不认为我想在这里做任何花哨的事情。

4

1 回答 1

22

我希望这是我犯的一个愚蠢的错误。

不幸的是(或幸运的是?)你犯了几个错误。

  1. 首先,您需要了解查询通知将使一个查询无效。因此,您最多只会收到一次通知,如果您想收到更多通知,则必须再次重新订阅(重新提交查询)。

  2. 接下来,您需要了解无论出于何种原因都会通知您,而不仅仅是更改。在您的回调中,您必须检查您收到通知的原因,这些原因是通过SqlNotificationEventArgs.

  3. 接下来,您需要了解异步编程的基本原则:如果您订阅了一个事件,请确保您在事件第一次发生之前订阅。举个例子:只要你提交查询就On_SqlBitChanged可以触发。这应该发生在构造函数中,但是您在构造函数运行订阅。可以在连接事件回调之前在构造函数完成之间调用,在这种情况下,通知将被静默忽略。SqlWatcher.SqlWatchersqlWatcher.NewMessage On_SqlBitChangedNewMessage

  4. 如果要使用服务,请确保在使用之前启动它。您正在使用 SqlDependency ,SqlWatcher.SqlWatcher调用SqlWatcher.Start().

  5. 最后,如果您想收到查询更改的通知,您必须提交查询。您正在构建SqlCommand对象,设置通知,然后...丢弃该对象。除非您实际提交查询,否则您还没有订阅任何内容

修复建议:

  • 制作StartStop静态,调用Start应用程序启动。
  • 确保您在提交查询NewMessage 之前订阅
  • 实际提交查询(调用SqlComamnd.ExecuteQuery()
  • 检查Info,TypeSourceOn_SqlBitChanged回调中,如果您的提交包含错误,这是唯一的学习方法(即使通知请求无效,SqlComamnd.ExecuteQuery() 也会成功)
  • 一旦收到更改通知,您必须重新订阅,再次执行查询。

还有一件事:不要在后台回调中调用 UI 代码。您不能MessageBox.Show("Message Received");从回调中调用,您必须通过表单主线程通过Form.Invoke. 是的,我知道严格来说MessageBox.Show 确实可以在非 UI 线程上工作,但您很快就会从警报框移到实际形成交互,然后事情就会中断。

于 2013-03-22T10:03:55.327 回答