2015-04-23 15 views
5

Ho sviluppato una pagina Web che si collega all'hub signalR che si collega al codice jquery con angularjs. Ai client viene inviato un messaggio quando si verifica un evento sqldependency.onchange, tuttavia, per ogni client connesso il messaggio viene duplicato a ciascuno. Quindi, se due client sono connessi, ciascun client riceve due messaggi e così via.Messaggi duplicati SignalR su sqlDipendenza cambia

Ecco i passaggi:

  • collegare due clienti al mozzo
  • fare il cambiamento DB
  • sqlDependency.onchange incendi
  • chiamata di funzione hub clients.all.renewProducts()
  • Ricrea dati respository azzeramento della dipendenza
  • Console client
  • : "Database SQL Dependency change detected: Update" app.js:44:12 (Twice)

Hub.cs

public static void SignalRGetData(string data) 
{ 
     IHubContext context = GlobalHost.ConnectionManager.GetHubContext<SignalRGetData>(); 
     context.Clients.All.renewData(data); 

     // Recereate data and sql dependency 
     new DataRespository().GetData(); 
} 

DataRespository.cs _dependency_OnChange

public void _dependency_OnChange(object sender, SqlNotificationEventArgs e) 
{ 
    if(e.Info == SqlNotificationInfo.Update) 
    { 
     ProductHub.GetProducts("Database SQL Dependency change detected: " + e.Info); 
    } 

}

GetData

public IEnumerable<ProductInventoryDetail> GetData() 
{ 
    using(var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DynamicPricing"].ConnectionString)) 
    { 
      conn.Open(); 
      var reposQuery = 
      "SELECT [ID], [Program] FROM [DBO].[Detail]"; 
      using(SqlCommand cmd = new SqlCommand(reposQuery, conn)) 
      { 
       // remove any command object notifications 
       cmd.Notification = null; 
       // create dependency 
       SqlDependency dependency = new SqlDependency(cmd); 
       dependency.OnChange += new OnChangeEventHandler(_dependency_OnChange); 

       if (conn.State == System.Data.ConnectionState.Closed) 
        conn.Open(); 

       // Execute Sql Command 
       using(var reader = cmd.ExecuteReader()) 
       { 
        return reader.Cast<IDataRecord>().Select(x => new ProductInventoryDetail(){ 

         ID = x.GetInt32(0), 
         Program = x.GetInt32(1) 
        } 
       } 
      } 
     } 
} 

angolare JavaScript

// Apply jQuery SignalR operations to Angular 
app.value('$', $); 

app.factory('signalRService', ['$', '$rootScope', function ($, $rootScope) { 

    var proxy = null; 
    var initialise = function() { 
     // Get Connection to SignalR Hub 
     var connection = $.hubConnection(); 

     // Create a Proxy 
     proxy = connection.createHubProxy('SignalRData'); 

     // Publish the event when server has a push notification 
     proxy.on('renewProducts', function (message) { 
      console.log("Database SQL Dependency change detectedgnalRGetData: " + message); 
      $rootScope.$emit('renewProducts', message); 
     }); 

     // Start Connection 
     connection.start().done(function() { 
      console.log("Conenction Ready - invoke proxy"); 
      proxy.invoke('SignalRGetData'); 
     }); 

    }; 

    return { 
     initialise: initialise 
    } 
}]); 
+0

È possibile che si stia effettuando la connessione due volte sulla stessa pagina? (chiamando initialize due volte in controller diversi). – sirrocco

+0

Sarebbe questo il caso definendo un servizio angolare? – goingsideways

+0

No, ma se nella stessa pagina sono presenti 2 controller e entrambi ricevono il segnaleRService ed entrambi chiamano il metodo di inizializzazione, aprono due connessioni. – sirrocco

risposta

2

Per quanto posso vedere, il codice ha un paio di problemi:

  • Ogni volta quando il client si connette al server, invoca SignalRGetData. Secondo il tuo codice SigalRGetData crea una nuova entità SqlDependency. Pertanto, se hai due clienti, avrai due entità SqlDependency o due abbonamenti database che significa che avrai due notifiche sul lato di ciascun cliente. Se si desidera inviare una notifica per ciascun client quando si verifica una modifica nel database, è necessario disporre solo di un'entità SqlDependency per l'applicazione.
  • Secondo il vostro codice avrete una notifica solo per il primo cambio di database (so che è strano, ma è true). Se si desidera ricevere le notifiche in modo continuo si dovrebbe fare resubscribtion:

MSDN contiene un esempio di come utilizzare lo SqlDependency here.Si noti come, analogamente all'uso SqlNotification, il cliente si prevede che iscriversi di nuovo se augura di essere ulteriormente notificati

  • SqlDependency ha la problems con perdite di memoria. Hovewer, puoi utilizzare una realizzazione open source della classe SqlDependency - SqlDependencyEx. Utilizza un trigger del database e una notifica nativa di Service Broker per ricevere eventi relativi alle modifiche della tabella. Con SqlDependecyEx è possibile monitorare INSERT, DELETE, UPDATE separatamente e ricevere dati effettivi modificati dati (xml) nell'oggetto args evento. Questo è un esempio di utilizzo:
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); 

Suggerimento:

Per evitare le notifiche duplicato sul lato client è meglio utilizzare esemplare una notifica per il controller/applicazione. Un esempio di codice dal mio ultimo progetto:

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. 
    } 
} 

Spero che questo aiuti.

+0

hai menzionato che ci sono più abbonamenti di dipendenza che si verificano su ogni connessione client. Come si controllerebbe un esistente? – goingsideways

+0

@snowcode Non lo so. Dipende dalla tua logica aziendale. – dyatchenko

1

Ho una soluzione 1. Basta creare una classe con un attributo statico bool.

public class OutilContext 
{ 
    public static bool first = true; 
} 
  1. Nella tua Global.asax.cs, si dovrebbe controllare il Session_Start vuoto

    if (OutilContext.first == true) 
    { 
         OutilContext.first = false; 
         NotificationComponent NC = new NotificationComponent(); 
         var currentTime = DateTime.Now; 
    
         NC.RegisterNotification(currentTime); 
    } 
    

che controllano una serie di SqlDependency, perché quando un client accede all'applicazione la classe globale crea SqlDipendenza per ogni client.