我们除了可以在集线器中向客户端推送消息,也可以在MVC控制器、托管服务等外部向客户端推送消息,这在一些场景中非常有用。
比如,在Web聊天室中,管理员通过控制器的操作方法把某一个群成员设置为群管理员了,我们就需要在操作方法中向所有客户推送这个消息;
再如,在后台的托管服务中,我们检测到数据发生变化了,就在托管服务中向客户端推送数据变化的通知。
下面来实现在后台中新增一个用户的时候,通知聊天室所有的客户端”欢迎新人XX加入”。
Hub类中可以从依赖注入容器中注入上下文服务,因此我们可以在ChatRoomHub中编写用户新增用户的AddUser方法,在AddUser中执行数据库插入新用户的操作,然后把消息推送给客户端。
这样在前端中新增用户的时候,我们直接通过SignalR调用ChatRoomHub中的AddUser方法来完成用户的新增。
不过,在使用SignalR的时候,Hub类中的方法不应该有数据库操作等比较耗时的操作,因为这会影响SignalR的性能,Hub类中的方法只应该用于消息的发布,而不应该用来写业务逻辑。
SignalR中客户端给服务器端传递消息的超时时间为30s,如果对Hub类中的方法的调用执行时间超过30s,程序就会报错。
虽然我们可以调整默认的超时时间,但是本书的作者强烈不建议这样做,因为如果出现Hub类中的方法执行时间很长,那么一定是在方法中编写了除了消息发布之外的代码。
因此,我们一般仍然在控制器中编写业务逻辑代码,只是通过集线器向客户端推送消息。
在Hub类之外,我们可以通过注入IHubContext<THub>来获取对集线器进行操作的服务,其中泛型THub为要操作的Hub类。
IHubContext中定义了可以进行客户端筛选的IHubClients类的Clients属性,以及进行分组操作的IGroupManager类的Groups属性,它们的用法和Hub类非常类似,这里不进行过多解释。
下面我们来实现WebAPI中完成新增用户操作后,向客户端推送消息的功能。
第1步
在ASP.NET Core Web API的控制器类中通过构造方法注入一个IHubContext服务,如以下代码所示:
//使用主构造函数注入IHubContext服务
public class ChatRoomHub(IHubContext _hubContext):Hub
第2步
为控制器类增加一个用于新增用户的操作方法,如以下代码所示:
public async Task AddUser(User user)
{
//这里省略执行用户注册的代码
//用SendAsync推送
await _hubContext.Clients.All.SendAsync("UserAdded", user.Name);
}
第3步
在前端中增加监听”UserAdded”消息的代码,并且把消息添加到聊天记录中,如以下代码所示:
connection.on{'UserAdded',userName=>{
state.message.push("系统消息:欢迎"+userName+"加入我们!");
});
完成上面的代码后,运行项目,然后在WebAPI页面中调用AddUser方法新增用户,就可以在聊天室页面中看到在控制器中向客户端推送的消息了。
IHubContext接口和Hub类有如下的区别:
IHubContext接口中没有Hub类中的上下文属性Context;
IHubContext接口的Clients属性是IHubClients类型的,而Hub类中的Client属性IHubCallerClients类型的。
SIgnalR注意事项:
- Hub类的生命周期是瞬态的
- 建议把ASP.NET Core程序和SignalR服务器端部署到不同的服务器上,以免它们互相干扰。