SignalR重复消息在sqlDependency上更改

时间:2015-04-23 23:31:44

标签: c# angularjs signalr sqldependency

我开发了一个连接到signalR hub的网页,它连接到带有angularjs的jquery代码。发生sqldependency.onchange事件时会向客户端发送消息,但是,对于连接的每个客户端,消息都会复制到每个客户端。因此,如果连接了两个客户端,则每个客户端都会收到两条消息,依此类推。

以下是步骤:

  • 将两个客户端连接到集线器
  • 进行数据库更改
  • 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)
                     }
                 }
           }
      }
}

Angular 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
    }
}]);

2 个答案:

答案 0 :(得分:2)

据我所知,您的代码有几个问题:

  • 每次客户端连接到服务器时,它都会调用 SignalRGetData。根据您的代码SigalRGetData创建一个 新的SqlDependency实体。因此,如果你有两个客户,你会 有两个SqlDependency实体或两个数据库订阅 意味着每个客户端都会有两个通知。如果您希望在数据库发生更改时为每个客户端发送一个通知,那么您的应用程序应该只有一个SqlDependency实体。
  • 根据您的代码,您只会收到第一次数据库更改的通知(我知道它很奇怪,但它是true)。如果您想要不断收到通知,则应该 resubscribtion
  

MSDN包含如何使用SqlDependency here的示例。请注意,与SqlNotification使用情况类似,如果客户端需要进一步通知

,那么客户应该再次订阅
  • SqlDependencyproblems内存泄漏。 Hovewer,您可以使用SqlDependency类的开源实现 - SqlDependencyEx。它使用数据库触发器和本机Service Broker通知来接收有关表更改的事件。使用SqlDependecyEx,您可以分别监控INSERTDELETEUPDATE,并接收实际更改的数据(xml)事件args对象。这是一个用法示例:
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.
    }
} 

希望这有帮助。

答案 1 :(得分:2)

我有一个解决方案 1.只需使用静态属性bool创建一个Class。

public class OutilContext
{
    public static bool first = true;
}
  1. 在Global.asax.cs中,您应该控制Session_Start void

    if (OutilContext.first == true)
    {
            OutilContext.first = false;
            NotificationComponent NC = new NotificationComponent();
            var currentTime = DateTime.Now;
    
            NC.RegisterNotification(currentTime); 
    }
    
  2. 它控制了许多SqlDependency,因为当客户端访问应用程序时,Global Class为每个客户端创建SqlDependency。