我有一个MVC 4站点,它使用SqlDependency检测数据库的更改,然后通过SignalR通知订阅的客户端更改。所有的机制都已到位,应用程序知道发生了变化,但在这里它变得有趣......我看到越来越多的客户端通知基于{之间发生的浏览器刷新次数{1}}和Application_Start()
,或已连接的客户端数量。
也许我的理解是关于这些技术的,但我认为SignalR变成了一个Singleton,它会导致所有流量发生在一个" pipe"客户端和服务器之间,无论连接的客户端数量是多少。
当然,这并不能解释为什么刷新似乎实例化一个全新的SqlDependency。
我看到this answer显示了关闭SqlDependency(模拟Application_End()
)的想法,但所做的只是在页面上添加执行时间并且没有解决问题。< / p>
我很难过,并且非常感谢一些让我们按预期工作的建议。
这是我正在使用的代码......
Application_End()
In my Global.asax.cs file:
using System.Configuration;
using System.Data.SqlClient;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace SmartAppV1
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// SignalR Wireup
SqlDependency.Start(ConfigurationManager.ConnectionStrings["SmartAppSignalR"].ConnectionString);
}
protected void Application_End()
{
// Shut down SignalR Dependencies
SqlDependency.Stop(ConfigurationManager.ConnectionStrings["SmartAppSignalR"].ConnectionString);
}
}
}
My SignalR Hub:
using System.Collections.Generic;
using System.Configuration;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using SmartAppData.Entities;
using SmartAppV1.Models;
namespace SmartAppV1
{
[HubName("smartAppHub")]
public class SmartAppHub : Hub
{
private readonly string _connection = ConfigurationManager.ConnectionStrings["SmartAppSignalR"].ConnectionString;
public void MonitorGrid4DataChanges()
{
var setGrid4 = new SmartAppSignalR
{
ConnectionString = _connection,
Query =
@"SELECT [ID], [OrdHeaderId], [LoadId], [NewStatus] FROM [dbo].[CTLoadStatusChangeLog] WHERE [NewStatus] = 'Delivered' ORDER BY [ID] DESC"
};
setGrid4.DispatchBoardStatusChange();
}
}
}
My SignalR class:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Microsoft.AspNet.SignalR;
using SmartAppData;
using SmartAppData.Entities;
using SmartAppData.Services;
namespace SmartAppV1.Models
{
public class SmartAppSignalR
{
public string ConnectionString { get; set; }
public string Query { get; set; }
public IEnumerable<DeliveredGridItem> ReadGrid4Data()
{
var service = new LoadService();
var result = service.GetLoadsByBookedByIdByTmsStatus(110, LoadTmsStatus.Delivered.ToString()).ToList();
var deliveredList = new List<DeliveredGridItem>();
foreach (var obj in result)
{
var deliveredItem = new DeliveredGridItem(obj.LoadId) { LoadTmsStatus = obj.DataValue_LoadTmsStatus };
deliveredList.Add(deliveredItem);
}
return deliveredList;
}
public void DispatchBoardStatusChange()
{
using (var conn = new SqlConnection(ConnectionString))
{
using (var cmd = new SqlCommand(Query, conn))
{
cmd.Notification = null;
var dependency = new SqlDependency(cmd);
// make sure the OnChange doesn't exist
// trying to remove redundant calls
dependency.OnChange -= dispatchBoard_OnChange;
dependency.OnChange += dispatchBoard_OnChange;
if (conn.State == ConnectionState.Closed)
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
}
}
}
}
private void dispatchBoard_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type != SqlNotificationType.Change) return;
// dump the original OnChange event handler...we're going to re-register it
var sqlDependency = (SqlDependency) sender;
sqlDependency.OnChange -= dispatchBoard_OnChange;
var retVal = new StatusChangedObject();
using (var conn = new SqlConnection(ConnectionString))
{
using (var cmd = new SqlCommand(Query, conn))
{
if (conn.State == ConnectionState.Closed)
conn.Open();
var reader = cmd.ExecuteReader();
if (reader.Read())
{
retVal.Id = Convert.ToInt32(reader["ID"]);
retVal.OrdHeaderId = Convert.ToInt32(reader["OrdHeaderId"]);
retVal.LoadId = Convert.ToInt32(reader["LoadId"]);
retVal.NewStatus = reader["NewStatus"].ToString();
var clients = GlobalHost.ConnectionManager.GetHubContext<SmartAppHub>().Clients;
clients.All.gridUpdate(retVal);
}
}
}
// Re-register the SqlDependency
DispatchBoardStatusChange();
}
}
public class StatusChangedObject
{
public int Id { get; set; }
public int OrdHeaderId { get; set; }
public int LoadId { get; set; }
public string NewStatus { get; set; }
}
}
注意:我使用Telerik网格,所以我必须在And, finally my .js code for the SignalR:
之外进行SignalR连接,这是我在其他所有教程/示例中连接SignalR的地方。
$(document).ready()
答案 0 :(得分:0)
好的,所以我认为这里有一些事情,你正在添加和删除事件来经常处理sqldependency更改。您应该使用保护逻辑来查看事件处理程序是否存在,删除不存在的处理程序有时会产生奇怪的影响。至于多次启动等,每次重新加载或离开客户端页面时它都会断开连接,并在加载时重新连接。信号器集线器不是单例实例,除非您以这种方式编码,默认情况下不会这样做。这是您想要做的简单版本。
try
{
using (
var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(@"SELECT [Id]
,[FName]
,[LName]
,[DOB]
,[Notes]
,[PendingReview]
FROM [dbo].[Users]",
connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
command.ExecuteReader();
}
}
}
catch (Exception e)
{
throw;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
JobHub.Show();
}
如果你想创建一个单例类型的集线器,你需要设置一个像这样的东西,你有一个静态集线器和一个集线器跟踪器类,这是在跟踪器类中。查看教程链接以获取更多详细信息:
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
private readonly ConcurrentDictionary<string, Stock> _stocks = new ConcurrentDictionary<string, Stock>();
查看本教程,将集线器用作单例: http://www.asp.net/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr