2015-04-23 70 views
5

我開發了一個網頁,連接到使用angularjs連接到jQuery代碼的signalR集線器。當發生一個sqldependency.onchange事件時,客戶端會發送一條消息,但是,對於連接的每個客戶端而言,消息都被複制到每個客戶端。因此,如果連接兩個客戶端,則每個客戶端都會收到兩條消息,依此類推。SqlDependency的SignalR重複消息更改

步驟如下:

  • 兩個客戶端連接到集線器
  • 製作DB變化
  • sqlDependency.onchange火災
  • 呼叫中心功能clients.all.renewProducts()
  • 重新創建數據程序存儲庫重置依賴
  • 客戶端控制檯:"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) 
        } 
       } 
      } 
     } 
} 

角的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

是否有可能在同一頁面上連接兩次? (通過在不同的控制器中初始化兩次)。 – sirrocco

+0

定義角度服務會是這種情況嗎? – goingsideways

+0

不,但是如果您在同一頁面上有兩個控制器,並且它們都獲得了signalRService並且都調用初始化方法 - 則會打開兩個連接。 – sirrocco

回答

2

據我所看到的,你的代碼有幾個問題:

  • 當你的客戶端連接到服務器的每一次調用 SignalRGetData。根據您的代碼SigalRGetData創建一個 新的SqlDependency實體。因此,如果您有兩個客戶端,您將有 有兩個SqlDependency實體或兩個數據庫訂閱,其中 表示您將在每個客戶端有兩個通知。如果您希望在數據庫發生更改時爲每個客戶端發送一個通知,則應該只有一個針對您的應用程序的SqlDependency實體。
  • 根據你的代碼,你將只有第一次數據庫更改(我知道這很奇怪,但它是true)的通知。如果你想收到通知連續你應該讓resubscribtion

的MSDN包含如何使用的SqlDependency here的樣本。注意怎麼樣,SqlNotification用法類似,客戶有望再次認購如果whishes進一步通知

  • SqlDependencyproblems內存泄漏。 Hovewer,你可以使用SqlDependency類的開源實現 - SqlDependencyEx。它使用數據庫觸發器和本機Service Broker通知來接收有關表更改的事件。使用SqlDependecyEx,您可以分別監控INSERT,DELETE,UPDATE,並在事件參數對象中分別接收實際更改的數據(xml)。這是一個使用示例:
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); 

建議:

爲了避免重複的客戶端通知,最好是使用一個通知典範爲控制器/應用。我上一個項目的代碼示例:

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

希望這有助於。

+0

您提到每個客戶端連接上都有多個依賴項訂閱。如何檢查現有的? – goingsideways

+0

@snowcode我不知道。這取決於您的業務邏輯。 – dyatchenko

1

我有一個解決方案 1.只需創建一個具有靜態屬性bool的類。

public class OutilContext 
{ 
    public static bool first = true; 
} 
  • 在你的Global.asax.cs,你應該控制在session_start空隙

    if (OutilContext.first == true) 
    { 
         OutilContext.first = false; 
         NotificationComponent NC = new NotificationComponent(); 
         var currentTime = DateTime.Now; 
    
         NC.RegisterNotification(currentTime); 
    } 
    
  • 即控制多個的SqlDependency的,因爲當客戶端訪問應用程序,全局類爲每個客戶端創建SqlDependency。