Событие SqlDependency OnChange срабатывает много раз для каждого отдельного события в базе данных.
Я разрабатываю систему уведомлений с использованием SqlDependency и signalR. Проблема, с которой я не могу справиться, заключается в том, что при изменении значения атрибута "IsOnline" в БД на "Истина" или "Ложь" в зависимости от состояния участника событие OnChange запускается много раз, впервые новый пользователь войти в систему, я получаю два уведомления, тогда во второй раз я получаю больше, как 4, то больше, чем больше. Количество уведомлений увеличивается с каждым новым входом или выходом. Я уверен, что проблема в SqlDependency не в SignalR, я собираюсь поделиться с вами частью моего кода.
Заранее спасибо.
[System.Web.Services.WebMethod]
public static IEnumerable<AttendeeList> GetAllUsers()
{
var AttendeeList = new List<AttendeeList>();
try
{
using (var connection = new SqlConnection(_connString))
{
connection.Open();
string str = "";
str += "SELECT [AttendeeID], ";
str += " [IsAllowToUploadDocuments],";
str += " [IsOnline], ";
str += " [AttendeeTypeName],";
str += " [UserName] ";
str += " FROM [dbo].[Meeting_Attendees] ";
str += " INNER JOIN [dbo].[aspnet_Users] ON [aspnet_Users].[UserId] = [Meeting_Attendees].[AttendeeID] ";
str += " INNER JOIN [dbo].[AttendeeType] ON [dbo].[AttendeeType].[AttendeeTypeID] = [dbo].[Meeting_Attendees].[AttendeeTypeID] ";
str += " WHERE [MeetingID]=@MeetingID ORDER BY [IsOnline] DESC";
using (var command = new SqlCommand(@str, connection))
{
SqlParameter prm = new SqlParameter("@MeetingID", SqlDbType.Int);
prm.Direction = ParameterDirection.Input;
prm.DbType = DbType.Int32;
prm.Value = Convert.ToInt32(Properties.Settings.Default.MeetingID);
command.Parameters.Add(prm);
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependencyUsers_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
AttendeeList.Add(item: new AttendeeList { UserName = (string)reader["UserName"], UserType = (string)reader["AttendeeTypeName"], IsOnline = (bool)reader["IsOnline"], IsAllowToUploadDocuments = (bool)reader["IsAllowToUploadDocuments"], IsCurrentUser = true ? (Guid)reader["AttendeeID"] == new Guid(Properties.Settings.Default.UserID.ToString()) : false });
}
}
}
}
catch { }
return AttendeeList;
}
private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update)
{
//Call SignalR
MessagesHub.UpdateUsers();
}
}
3 ответа
Чтобы обработчик событий был зарегистрирован один раз, поставьте "-=" перед "+=":
oDependency.OnChange -= new OnChangeEventHandler(DBUpdateNotificationReeived);
oDependency.OnChange += new OnChangeEventHandler(DBUpdateNotificationReeived);
Проверьте, нет ли в таблице ur SQL триггеров (кроме sqltabledependency), которые "обновляют" обновляемую запись.
У меня тоже была такая же проблема с несколькими звонками на OnChange
событие в моем проекте, но я исправил это с помощью переменной counter. Следуя этому примеру, в моем случае dependencyUsers_OnChange
Функция из примера стреляла дважды.
Я инициализировал переменную counter как глобальную. После "сканирования" состояния ваших временных данных перед любым изменением я также установил значение счетчика на 0.
Следуя вашему примеру, после этого шага, изменение было сделано в dependencyUsers_OnChange
в if
заявление:
private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update && counter == 0)
{
//Call SignalR
MessagesHub.UpdateUsers();
counter++; //The update is done once
}
else
{
counter = 0; //if the update is needed in the same iteration, please don't update and set the counter to 0
}
}
В вашем случае решение будет примерно таким:
int counter = 0; //initialization of help counter
[System.Web.Services.WebMethod]
public static IEnumerable<AttendeeList> GetAllUsers()
{
var AttendeeList = new List<AttendeeList>();
try
{
using (var connection = new SqlConnection(_connString))
{
connection.Open();
string str = "";
str += "SELECT [AttendeeID], ";
str += " [IsAllowToUploadDocuments],";
str += " [IsOnline], ";
str += " [AttendeeTypeName],";
str += " [UserName] ";
str += " FROM [dbo].[Meeting_Attendees] ";
str += " INNER JOIN [dbo].[aspnet_Users] ON [aspnet_Users].[UserId] = [Meeting_Attendees].[AttendeeID] ";
str += " INNER JOIN [dbo].[AttendeeType] ON [dbo].[AttendeeType].[AttendeeTypeID] = [dbo].[Meeting_Attendees].[AttendeeTypeID] ";
str += " WHERE [MeetingID]=@MeetingID ORDER BY [IsOnline] DESC";
using (var command = new SqlCommand(@str, connection))
{
SqlParameter prm = new SqlParameter("@MeetingID", SqlDbType.Int);
prm.Direction = ParameterDirection.Input;
prm.DbType = DbType.Int32;
prm.Value = Convert.ToInt32(Properties.Settings.Default.MeetingID);
command.Parameters.Add(prm);
command.Notification = null;
var dependency = new SqlDependency(command);
counter = 0; //Whenewer the web method is called, set te counter to 0
dependency.OnChange += new OnChangeEventHandler(dependencyUsers_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
AttendeeList.Add(item: new AttendeeList { UserName = (string)reader["UserName"], UserType = (string)reader["AttendeeTypeName"], IsOnline = (bool)reader["IsOnline"], IsAllowToUploadDocuments = (bool)reader["IsAllowToUploadDocuments"], IsCurrentUser = true ? (Guid)reader["AttendeeID"] == new Guid(Properties.Settings.Default.UserID.ToString()) : false });
}
}
}
}
catch { }
return AttendeeList;
}
private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update && counter == 0)
{
//Call SignalR
MessagesHub.UpdateUsers();
counter++; //The update is done once
}
else
{
counter = 0; //if the update is needed in the same iteration, please don't update and set the counter to 0
}
}
Я надеюсь, что эта идея будет полезна для кого-то, я решил проблему в своем проекте с этим решением.
Я попадаю в ту же проблему при использовании SignalR и SQL Dependency
Строка исполнялась не раз. На событие следует подписаться только один раз. oDependency.OnChange += new OnChangeEventHandler(DBUpdateNotificationReeived);
Уведомления о запросах запускаются при изменении отслеживаемого набора результатов, см. Раздел "Понимание того, когда появляются уведомления о запросах". Как правило, вы можете получить больше уведомлений, чем фактических изменений данных:
Обратите внимание, что SQL Server может выдавать уведомление о запросе в ответ на события, которые не изменяют данные, или в ответ на изменение, которое фактически не влияет на результаты запроса. Например, когда инструкция UPDATE изменяет одну из строк, возвращаемых запросом, уведомление может сработать, даже если обновление строки не изменило столбцы в результатах запроса. Уведомления о запросах предназначены для поддержки общей цели улучшения производительность для приложений, которые кэшируют данные. Когда сервер сильно загружен, SQL Server может создать сообщение с уведомлением о запросе для подписки, а не выполнять работу по определению, изменились ли результаты запроса.
О том, вызывает ли это проблемы в вашем случае, невозможно сказать из вашего поста, в частности, неясно, как вы обрабатываете обновления, так что это приводит к еще 4 уведомлениям. Получение 4 уведомлений подразумевает, что вы отправили 4 запроса на уведомление, поэтому, скорее всего, у вас также есть проблема с кодом и вы переподписались.
Прочтите Использование трассировки SQL для устранения неполадок уведомлений о запросах и попробуйте выяснить, что происходит, где создаются и аннулируются уведомления.