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 ответ
Я надеюсь, что это глупая ошибка, которую я сделал.
К сожалению (или к счастью?) Вы делаете несколько ошибок.
Во-первых, вы должны понимать, что Query Notification сделает недействительным один запрос. Таким образом, вы будете уведомлены не более одного раза, и вам придется повторно подписаться (повторно отправить запрос), если вы хотите получать дополнительные уведомления.
Далее вам нужно понять, что вы будете уведомлены по любой причине, а не только об изменениях. В вашем обратном вызове вы должны проверить причину, по которой вы получили уведомление, которые передаются через
SqlNotificationEventArgs
,Далее вам необходимо понять основные принципы асинхронного программирования: если вы подписываетесь на событие, убедитесь, что вы подписались, прежде чем событие может произойти в первый раз. Показательный пример:
On_SqlBitChanged
может стрелять, как только вы отправите запрос. Это должно произойти вSqlWatcher.SqlWatcher
конструктор, но вы подписались наsqlWatcher.NewMessage
после запуска конструктора.On_SqlBitChanged
может быть вызван между завершениями конструктора, прежде чем подключитьNewMessage
обратный вызов события, в этом случае уведомление молча игнорируется.Если вы хотите использовать сервис, убедитесь, что вы запустите его, прежде чем использовать его. Вы используете SqlDependency в
SqlWatcher.SqlWatcher
но вы начинаете это после этого, когда вы звонитеSqlWatcher.Start()
,Наконец, если вы хотите получать уведомления об изменениях в запросе, вы должны отправить запрос. Вы строите
SqlCommand
объект, настроить уведомление, а затем... отказаться от объекта. Если вы на самом деле не отправили запрос, вы еще ни на что не подписались.
Предложения по исправлению:
- Делать
Start
а такжеStop
статика, звонокStart
в приложении запустить. - Убедитесь, что вы подписаны на
NewMessage
прежде чем отправить запрос - Собственно отправить запрос (позвонить
SqlComamnd.ExecuteQuery()
) - Осмотреть
Info
,Type
а такжеSource
вOn_SqlBitChanged
обратный вызов, если ваше представление содержит ошибку, это единственный способ узнать (SqlComamnd.ExecuteQuery() будет успешным, даже если запрос на уведомление недействителен) - Вы должны повторно подписаться, как только вы получите уведомление об изменении, выполните запрос еще раз.
Еще одна вещь: не вызывайте код пользовательского интерфейса в фоновых обратных вызовах. Вы не можете позвонить MessageBox.Show("Message Received");
из обратного вызова, вы должны направить через основной поток формы через Form.Invoke
, Да, я знаю, что строго говоря MessageBox.Show
работает в потоке, не являющемся пользовательским интерфейсом, но вы скоро отойдете от окон предупреждений, чтобы фактически сформировать взаимодействие, и тогда все будет сломано