SqlDependency OnChange不触发

6

这是我第一次使用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 的子集可以用于依赖项,但我不认为我在这里尝试做任何太复杂的事情。


认为任何试图寻找sqldependency的解决办法的人,应该使用sqldependencyEx。可以在此处找到简单的连接步骤。https://github.com/dyatchenko/ServiceBrokerListener - kepung
1个回答

23

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

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

  1. 首先你需要明白查询通知将使一个查询无效。所以你最多只能得到一次通知,如果想要接收更多通知,必须重新订阅(重新提交查询)。

  2. 其次你需要明白,你会因为任何原因而被通知,而不仅仅是因为数据变化。在回调函数中,你必须检查你被通知的原因,这些原因通过SqlNotificationEventArgs传递。

  3. 接下来你需要理解异步编程的基本原则:如果你订阅事件,请确保在事件可能发生的第一时间之前进行订阅。比如:On_SqlBitChanged可以在你提交查询后立即触发。这应该SqlWatcher.SqlWatcher构造函数中发生,但你在构造函数运行之后才订阅了sqlWatcher.NewMessage事件。在这种情况下,如果在钩子上添加NewMessage事件回调之前就调用了On_SqlBitChanged,则通知将被忽略。

  4. 如果你要使用服务,请在使用前先启动它。你正在SqlWatcher.SqlWatcher中使用SqlDependency,但你在调用SqlWatcher.Start()之后才启动它。

  5. 最后,如果你想要接收查询的变化通知,必须提交查询。你构造了SqlCommand对象,设置了通知,然后...丢弃了对象。除非你实际提交查询,否则你还没有订阅任何东西。

修复建议:

  • StartStop设置为静态方法,在应用程序启动时调用Start
  • 确保在提交查询之前订阅NewMessage事件。
  • 实际提交查询(调用SqlComamnd.ExecuteQuery())。
  • On_SqlBitChanged回调中检查InfoTypeSource,如果提交包含错误,则只有这种方式可以了解(即使通知请求无效,SqlComamnd.ExecuteQuery()也会成功)。
  • 一旦收到更改通知就必须重新订阅,并再次执行查询。

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


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