SignalR duplicate messages on sqlDependency change
As far as I can see, your code has a couple of problems:
- Every time when your client connects to the server it invokes
SignalRGetData
. According to your codeSigalRGetData
creates anewSqlDependency
entity. Thus, if you have two clients you willhave twoSqlDependency
entities or two database subscriptions whichmeans you will have two notifications on the side of each client. If you want to send one notification for each client when a change in the database occurs you should have just only oneSqlDependency
entity for your application. - According to your code you will have a notification just only for the first database change (I know it is strange, but it is true). If you want to receive notifications continuously you should make resubscribtion:
The MSDN contains a sample of how to use the SqlDependency here. Note how, similarly to the SqlNotification usage, the client is expected to subscribe again if it whishes to be further notified
SqlDependency
has the problems with memory leaks. Hovewer, you can use an open source realization of theSqlDependency
class - SqlDependencyEx. It uses a database trigger and native Service Broker notification to receive events about the table changes. WithSqlDependecyEx
you are able to monitorINSERT
,DELETE
,UPDATE
separately and receive actual changed data (xml
) in the event args object. This is an usage example:
int changesReceived = 0;using (SqlDependencyEx sqlDependency = new SqlDependencyEx( TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME)) { sqlDependency.TableChanged += (o, e) => changesReceived++; sqlDependency.Start(); // Make table changes. MakeTableInsertDeleteChanges(changesCount); // Wait a little bit to receive all changes. Thread.Sleep(1000);}Assert.AreEqual(changesCount, changesReceived);
Suggestion:
To avoid duplicated client-side notifications it is better to use one notification exemplar for your controller/application. A code example from my last project:
public class HomeController : Controller{ // One global subscription for all the controller. static HomeController() { // ITableRowRepository incapsulates SqlDependencyEx usage. var repo = (ITableRowRepository)DependencyResolver.Current .GetService(typeof(ITableRowRepository)); // One global subscription. repo.TableChanged += RepoTableChanged; } // Actions here. private static void RepoTableChanged(object sender, TableChangedEventArgs e) { // Clients notification here. }}
Hope this helps.
I have a solution1. Just create a Class with a static attribute bool.
public class OutilContext{ public static bool first = true;}
In your Global.asax.cs, You should control the Session_Start void
if (OutilContext.first == true){ OutilContext.first = false; NotificationComponent NC = new NotificationComponent(); var currentTime = DateTime.Now; NC.RegisterNotification(currentTime); }
That control a number of SqlDependency, because when a client accesses the application the Global Class create SqlDependency for each client.