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 を実行すると、「メッセージを受信しました」というメッセージ ボックスが表示されることです。

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

何を確認/変更するかについてのヒントを誰かに教えてもらえますか?

依存関係で使用できるのは Sql 機能のサブセットのみであることは承知していますが、ここで何か特別なことをしようとしているとは思いません。

4

1 に答える 1

22

私が犯した愚かな間違いであることを願っています。

残念ながら (または幸いなことに?)、いくつかの間違いを犯しています。

  1. まず、クエリ通知が1 つのクエリを無効にすることを理解する必要があります。そのため、通知は最大で1 回しか送信されず、さらに通知を受け取りたい場合は、再度サブスクライブする (クエリを再送信する) 必要があります。

  2. 次に、変更だけでなく、何らかの理由で通知されることを理解する必要があります。コールバックで、通知された理由を確認する必要SqlNotificationEventArgsがあります。これは、 .

  3. 次に、非同期プログラミングの基本原則を理解する必要があります。イベントをサブスクライブする場合は、イベントが最初に発生する前にサブスクライブしてください。適切な例: は、クエリを送信するとすぐに起動できOn_SqlBitChangedます。これはコンストラクターで発生するはずですが、コンストラクターの実行にサブスクライブします。イベントコールバックをフックする前に、コンストラクターの終了間で呼び出すことができます。この場合、通知は黙って無視されます。SqlWatcher.SqlWatchersqlWatcher.NewMessage On_SqlBitChangedNewMessage

  4. サービスを使用する場合は、使用する前にサービスを開始してください。SqlDependency を使用していますが、その後を呼び出すときにSqlWatcher.SqlWatcher開始します。SqlWatcher.Start()

  5. 最後に、クエリの変更について通知を受けたい場合は、クエリを送信する必要があります。オブジェクトを構築SqlCommandし、通知を設定してから...オブジェクトを破棄します。実際にクエリを送信しない限り、まだ何も購読していません。

修正の提案:

  • MakeStartStopstatics はStart、アプリケーションの起動時に呼び出します。
  • クエリを送信するNewMessage 前に、必ず購読してください
  • 実際にクエリを送信 (呼び出しSqlComamnd.ExecuteQuery())
  • を検査し、Infoコールバックで、送信にエラーが含まれている場合、これが学習する唯一の方法です (通知要求が無効であっても SqlComamnd.ExecuteQuery() は成功します)。TypeSourceOn_SqlBitChanged
  • 変更が通知されたら、再登録する必要があります。クエリを再度実行してください。

もう 1 つ: バックグラウンド コールバックで UI コードを呼び出さないでください。コールバックから呼び出すことはできません。MessageBox.Show("Message Received");フォームのメイン スレッドを介してルーティングする必要がありますForm.Invoke。はい、厳密に言えば、非 UI スレッドで機能することはわかってMessageBox.Show ますが、すぐにアラート ボックスから離れて実際に対話を形成すると、問題が発生します。

于 2013-03-22T10:03:55.327 に答える