SqlDependency OnChange не стреляет

Это первый раз, когда мне нужно было использовать SqlDependency, поэтому я надеюсь, что это глупая ошибка, которую я сделал.

У меня проблема в том, что событие 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, будет отображаться MessageBox с сообщением "Сообщение получено"

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

Может ли кто-нибудь дать мне подсказку о том, что проверить / изменить?

Я знаю, что в зависимостях можно использовать только подмножество функций Sql, но я не думаю, что пытаюсь что-то сделать здесь, чтобы придумать.

1 ответ

Решение

Я надеюсь, что это глупая ошибка, которую я сделал.

К сожалению (или к счастью?) Вы делаете несколько ошибок.

  1. Во-первых, вы должны понимать, что Query Notification сделает недействительным один запрос. Таким образом, вы будете уведомлены не более одного раза, и вам придется повторно подписаться (повторно отправить запрос), если вы хотите получать дополнительные уведомления.

  2. Далее вам нужно понять, что вы будете уведомлены по любой причине, а не только об изменениях. В вашем обратном вызове вы должны проверить причину, по которой вы получили уведомление, которые передаются через SqlNotificationEventArgs,

  3. Далее вам необходимо понять основные принципы асинхронного программирования: если вы подписываетесь на событие, убедитесь, что вы подписались, прежде чем событие может произойти в первый раз. Показательный пример: On_SqlBitChanged может стрелять, как только вы отправите запрос. Это должно произойти в SqlWatcher.SqlWatcher конструктор, но вы подписались на sqlWatcher.NewMessage после запуска конструктора. On_SqlBitChanged может быть вызван между завершениями конструктора, прежде чем подключить NewMessage обратный вызов события, в этом случае уведомление молча игнорируется.

  4. Если вы хотите использовать сервис, убедитесь, что вы запустите его, прежде чем использовать его. Вы используете SqlDependency в SqlWatcher.SqlWatcher но вы начинаете это после этого, когда вы звоните SqlWatcher.Start(),

  5. Наконец, если вы хотите получать уведомления об изменениях в запросе, вы должны отправить запрос. Вы строите SqlCommand объект, настроить уведомление, а затем... отказаться от объекта. Если вы на самом деле не отправили запрос, вы еще ни на что не подписались.

Предложения по исправлению:

  • Делать Start а также Stop статика, звонок Start в приложении запустить.
  • Убедитесь, что вы подписаны на NewMessage прежде чем отправить запрос
  • Собственно отправить запрос (позвонить SqlComamnd.ExecuteQuery())
  • Осмотреть Info, Type а также Source в On_SqlBitChanged обратный вызов, если ваше представление содержит ошибку, это единственный способ узнать (SqlComamnd.ExecuteQuery() будет успешным, даже если запрос на уведомление недействителен)
  • Вы должны повторно подписаться, как только вы получите уведомление об изменении, выполните запрос еще раз.

Еще одна вещь: не вызывайте код пользовательского интерфейса в фоновых обратных вызовах. Вы не можете позвонить MessageBox.Show("Message Received"); из обратного вызова, вы должны направить через основной поток формы через Form.Invoke, Да, я знаю, что строго говоря MessageBox.Show работает в потоке, не являющемся пользовательским интерфейсом, но вы скоро отойдете от окон предупреждений, чтобы фактически сформировать взаимодействие, и тогда все будет сломано

Другие вопросы по тегам