为每个客户端创建一个单独的 SignalR Hub 实例并传递参数
Creating a separate SignalR Hub instance for each client and passing parameters
我正在尝试创建一个页面,当数据库发生变化时实时更新数据。我用过 SignalR。只有一个集线器,这里是集线器的代码:
public class MyHub : Hub
{
public static int prodId { get; set; }
public void setProdID(int pid)
{
prodId = pid;
}
public BidDetailViewModel GetChanges()
{
string conStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlConnection connection = new SqlConnection(conStr);
SqlDependency.Start(conStr);
string query = @"select Id,
BidDate,
BidAmount,
BidStatusId,
BidderId,
ProductId
from [dbo].[Bids]
where ProductId = " + prodId +
" order by BidDate desc ";
SqlCommand command = new SqlCommand(query, connection);
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
//If Something will change in database and it will call dependency_OnChange method.
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
connection.Open();
SqlDataReader dr = command.ExecuteReader();
var bid = new Bid();
while (dr.Read())
{
bid.Id = dr.GetInt32(0);
bid.BidDate = DateTime.Now; //fake value, dont need it
bid.BidAmount = dr.GetFloat(2);
bid.BidStatusId = dr.GetInt32(3);
bid.BidderId = dr.GetString(4);
bid.ProductId = dr.GetInt32(5);
//Break after reading the first row
//Using this becasue we can not use TOP(1) in the query due to SignalR restrictions
break;
}
connection.Close();
var vm = new BidDetailViewModel
{
HighestBid = bid,
NoOfBids = -1
//NoOfBids is no longer needed, assigning a random value of -1
//will remove it later, just testing rn
};
return vm;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change) SendNotifications();
}
private void SendNotifications()
{
BidDetailViewModel vm = GetChanges();
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
//Will update all the client with new bid values
context.Clients.All.broadcastMessage(vm);
}
}
我在两个不同的页面中调用集线器。第一个是 /Home/About
,第二个是 /Home/About2
。它们都具有相同的 JS 代码,除了一个变化,在 /Home/About
中传递的 prodId 是 40
,而对于 /Home/About2
,它是 41
。 JS 代码是:(这是 /Home/About
的代码,您可以看到它正在发送 40
。同样,/Home/About2
具有完全相同的代码,但带有 41
)。
<script>
$(function () {
// Declare a proxy to reference the hub.
var ew = $.connection.myHub;
//This method will fill all the Messages in case of any database change.
ew.client.broadcastMessage = function (response) {
//Changing HTML content here using document.getElementById()
};
//This method will fill all the Messages initially
$.connection.hub.start().done(function () {
ew.server.setProdID(40);
//Hardcoding the prodID for now, will get it using document.getElementById() later
//ProdId will be different for each page
ew.server.getChanges().done(function(response) {
//Changing HTML content here using document.getElementById()
});
});
});
</script>
如您所见,我正在通过调用 SetProdID
函数在 MyHub
集线器中设置 ProdId
。我已将 ProdId
设置为静态成员,因此我可以在不创建集线器对象的情况下进行设置。现在,如果访问 /Home/About
,它会将 prodId
设置为 40
(如预期的那样),然后我从查询中获得结果。但是,当我访问 \Home\About2
时,它将 prodId
设置为 41
(同样,正如预期的那样),并且我从与参数 41
相对应的查询中获得了结果。
现在,如果我在数据库中更新 ID 为 41
的项目,/Home/About
和 /Home/About2
页面都会实时更新,但会更新与项目对应的数据编号 41
。我知道这个 post 真的很长,但我的问题是,SignalR 不应该为两个不同的页面创建一个完全独立的 MyHub1
实例吗?发生这种情况是因为我已将 ProdId
设为静态成员吗?如果是这样,我该如何设置它而不使其成为静态成员?我基本上想要相同 MyHub
的多个实例,每个实例都完全不同 prodId
。这可能吗?还是我必须编写多个集线器(MyHub1、MyHub2、.... MyHubn)。编写多个集线器的问题是我不知道会有多少项目。
我也试过将 prodId
作为参数传递给 GetChanges()
方法,但我不确定在调用 [=43] 的 sendNotification()
方法中传递什么=] 方法。
public class MyHub : Hub
{
public BidDetailViewModel GetChanges(int pid)
{
............
string query = @"select Id,
BidDate,
BidAmount,
BidStatusId,
BidderId,
ProductId
from [dbo].[Bids]
where ProductId = " + pid +
" order by BidDate desc ";
...............
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change) SendNotifications();
}
private void SendNotifications()
{
//What do I pass here?
BidDetailViewModel vm = GetChanges();
..............
}
}
我不确定如何从这里开始,任何指点将不胜感激!另外,感谢阅读这篇长文 post.
SignalR 集线器是暂时的。这意味着 SignalR 为每个请求创建一个新的集线器实例。请注意,因此您不能在集线器实例中保留集线器特定状态(例如,在非静态 属性 中),因为一旦您的集线器方法完成,集线器将被处置并且您存储的值将丢失.
您的问题是您将 id 存储在静态变量中。静态成员在给定 class 的所有实例之间共享,因此如果您在一个实例中更改它,其他实例将看到新值。您可以在此 article 中阅读有关静态 classes 和成员的更多信息。
解决问题的一种方法是将状态保存在 ConcurrentDictionary
上下文中的静态变量中,其中您使用 connectionId 作为键。您可以使用 Context.ConnectionId
.
获取标识调用当前集线器方法的客户端的 connectionId
我正在尝试创建一个页面,当数据库发生变化时实时更新数据。我用过 SignalR。只有一个集线器,这里是集线器的代码:
public class MyHub : Hub
{
public static int prodId { get; set; }
public void setProdID(int pid)
{
prodId = pid;
}
public BidDetailViewModel GetChanges()
{
string conStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlConnection connection = new SqlConnection(conStr);
SqlDependency.Start(conStr);
string query = @"select Id,
BidDate,
BidAmount,
BidStatusId,
BidderId,
ProductId
from [dbo].[Bids]
where ProductId = " + prodId +
" order by BidDate desc ";
SqlCommand command = new SqlCommand(query, connection);
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
//If Something will change in database and it will call dependency_OnChange method.
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
connection.Open();
SqlDataReader dr = command.ExecuteReader();
var bid = new Bid();
while (dr.Read())
{
bid.Id = dr.GetInt32(0);
bid.BidDate = DateTime.Now; //fake value, dont need it
bid.BidAmount = dr.GetFloat(2);
bid.BidStatusId = dr.GetInt32(3);
bid.BidderId = dr.GetString(4);
bid.ProductId = dr.GetInt32(5);
//Break after reading the first row
//Using this becasue we can not use TOP(1) in the query due to SignalR restrictions
break;
}
connection.Close();
var vm = new BidDetailViewModel
{
HighestBid = bid,
NoOfBids = -1
//NoOfBids is no longer needed, assigning a random value of -1
//will remove it later, just testing rn
};
return vm;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change) SendNotifications();
}
private void SendNotifications()
{
BidDetailViewModel vm = GetChanges();
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
//Will update all the client with new bid values
context.Clients.All.broadcastMessage(vm);
}
}
我在两个不同的页面中调用集线器。第一个是 /Home/About
,第二个是 /Home/About2
。它们都具有相同的 JS 代码,除了一个变化,在 /Home/About
中传递的 prodId 是 40
,而对于 /Home/About2
,它是 41
。 JS 代码是:(这是 /Home/About
的代码,您可以看到它正在发送 40
。同样,/Home/About2
具有完全相同的代码,但带有 41
)。
<script>
$(function () {
// Declare a proxy to reference the hub.
var ew = $.connection.myHub;
//This method will fill all the Messages in case of any database change.
ew.client.broadcastMessage = function (response) {
//Changing HTML content here using document.getElementById()
};
//This method will fill all the Messages initially
$.connection.hub.start().done(function () {
ew.server.setProdID(40);
//Hardcoding the prodID for now, will get it using document.getElementById() later
//ProdId will be different for each page
ew.server.getChanges().done(function(response) {
//Changing HTML content here using document.getElementById()
});
});
});
</script>
如您所见,我正在通过调用 SetProdID
函数在 MyHub
集线器中设置 ProdId
。我已将 ProdId
设置为静态成员,因此我可以在不创建集线器对象的情况下进行设置。现在,如果访问 /Home/About
,它会将 prodId
设置为 40
(如预期的那样),然后我从查询中获得结果。但是,当我访问 \Home\About2
时,它将 prodId
设置为 41
(同样,正如预期的那样),并且我从与参数 41
相对应的查询中获得了结果。
现在,如果我在数据库中更新 ID 为 41
的项目,/Home/About
和 /Home/About2
页面都会实时更新,但会更新与项目对应的数据编号 41
。我知道这个 post 真的很长,但我的问题是,SignalR 不应该为两个不同的页面创建一个完全独立的 MyHub1
实例吗?发生这种情况是因为我已将 ProdId
设为静态成员吗?如果是这样,我该如何设置它而不使其成为静态成员?我基本上想要相同 MyHub
的多个实例,每个实例都完全不同 prodId
。这可能吗?还是我必须编写多个集线器(MyHub1、MyHub2、.... MyHubn)。编写多个集线器的问题是我不知道会有多少项目。
我也试过将 prodId
作为参数传递给 GetChanges()
方法,但我不确定在调用 [=43] 的 sendNotification()
方法中传递什么=] 方法。
public class MyHub : Hub
{
public BidDetailViewModel GetChanges(int pid)
{
............
string query = @"select Id,
BidDate,
BidAmount,
BidStatusId,
BidderId,
ProductId
from [dbo].[Bids]
where ProductId = " + pid +
" order by BidDate desc ";
...............
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change) SendNotifications();
}
private void SendNotifications()
{
//What do I pass here?
BidDetailViewModel vm = GetChanges();
..............
}
}
我不确定如何从这里开始,任何指点将不胜感激!另外,感谢阅读这篇长文 post.
SignalR 集线器是暂时的。这意味着 SignalR 为每个请求创建一个新的集线器实例。请注意,因此您不能在集线器实例中保留集线器特定状态(例如,在非静态 属性 中),因为一旦您的集线器方法完成,集线器将被处置并且您存储的值将丢失.
您的问题是您将 id 存储在静态变量中。静态成员在给定 class 的所有实例之间共享,因此如果您在一个实例中更改它,其他实例将看到新值。您可以在此 article 中阅读有关静态 classes 和成员的更多信息。
解决问题的一种方法是将状态保存在 ConcurrentDictionary
上下文中的静态变量中,其中您使用 connectionId 作为键。您可以使用 Context.ConnectionId
.