SqlDependency OnChange未触发

时间:2013-03-22 09:35:57

标签: c# sql sql-server sqldependency

这是我第一次需要使用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,将显示一个消息框,说“收到消息”

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

有人能给我一个关于检查/更改内容的提示吗?

我知道只有一部分Sql功能可用于依赖项,但我认为我不想在这里做任何事情。

1 个答案:

答案 0 :(得分:22)

  

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

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

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

  2. 接下来,您需要了解您将收到任何原因的通知,而不仅仅是更改。在您的回调中,您必须检查您收到通知的原因,这些原因是通过SqlNotificationEventArgs传递的。

  3. 接下来,您需要了解异步编程的基本原则:如果您订阅了某个事件,请确保在之前订阅,该事件可能会在第一时间发生。例证:On_SqlBitChanged可以在您提交查询后立即触发 。这个应该SqlWatcher.SqlWatcher构造函数中发生,但是在构造函数运行之后您订阅了sqlWatcher.NewMessage 。在挂钩On_SqlBitChanged事件回调之前,可以在构造函数完成之间调用NewMessage,在这种情况下,通知会被忽略。

  4. 如果您想使用某项服务,请确保在 之前启动它。您正在SqlWatcher.SqlWatcher中使用SqlDependency,但是在之后启动它,当您调用SqlWatcher.Start()时。

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

  6. 修复建议:

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

    还有一件事:不要在后台回调中调用UI代码。您无法从回调中调用MessageBox.Show("Message Received");,您必须通过Form.Invoke在表单主线程中进行路由。是的,我知道严格来说MessageBox.Show 在非UI线程上工作但你很快就会从警告框中移开来实际形成交互,然后事情就会破裂。