2013-03-22 276 views
3

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

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

下面是代码

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特性的一个子集可以在依赖中使用,但我不认为我想在这里做任何事情。

+0

认为任何人试图寻找sqldependency的解决方法,应使用sqldependencyEx。简单的连接步骤可以在这里找到。 https://github.com/dyatchenko/ServiceBrokerListener – kepung 2017-04-19 22:41:21

回答

19

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

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

  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线程上,但是你很快就会离开警报箱来实际形成交互,然后事情就会中断。