同样的,能不能让服务端声明一个强类型的方法列表给客户端调用呢?
如果要让客户端的方法以强类型出现在服务端,同样的,服务端的方法也以强类型出现在客户端,那就必须声明类似契约一样的载体。比如:
public interface IChatClient
{
void broadcast(string name, string message);
}
public interface IChatHub
{
void Send(string name, string message);
}
分别建立ChatClient接口和ChatHub的接口。
public class ChatHub : Hub
{
...
}
这是最终的目标,一个泛型Hub。
好,现在需要进行一些分析,怎样才能让Hub支持泛型。
首先,看一下Hub是如何操作客户端方法的:
Clients.AllExcept(Context.ConnectionId).broadcast(name, message);
Hub通过Clients来操作所有客户端的行为。那么这个Clients又是什么类型的呢?
// 摘要:
// Gets a dynamic object that represents all clients connected to this hub (not
// hub instance).
IHubCallerConnectionContext Clients { get; set; }
通过IHub接口看到,Clients的类型是IHubCallerConnectionContext,点进去看:
复制代码
// 摘要:
// Encapsulates all information about an individual SignalR connection for an
// Microsoft.AspNet.SignalR.Hubs.IHub.
public interface IHubCallerConnectionContext : IHubConnectionContext
{
[Dynamic]
dynamic Caller { get; }
[Dynamic]
dynamic Others { get; }
dynamic OthersInGroup(string groupName);
dynamic OthersInGroups(IList groupNames);
}
复制代码
IHubCallerConnectionContext又继承IHubConnectionContext,再点进去看:
复制代码
// 摘要:
// Encapsulates all information about a SignalR connection for an Microsoft.AspNet.SignalR.Hubs.IHub.
public interface IHubConnectionContext
{
[Dynamic]
dynamic All { get; }
dynamic AllExcept(params string[] excludeConnectionIds);
dynamic Client(string connectionId);
dynamic Clients(IList connectionIds);
dynamic Group(string groupName, params string[] excludeConnectionIds);
dynamic Groups(IList groupNames, params string[] excludeConnectionIds);
dynamic User(string userId);
}
复制代码
一目了然,所有Clients的操作方法都在这儿了,全是动态类型的,这也是为什么在Hub中写到Clients.All.xxx的时候已经是动态的了,那么运行时,这些操作都是什么类型的呢?试一下:
image
运行时,Clients的操作返回的是ClientProxy类型,从代码中扒出来:
复制代码
public class ClientProxy : DynamicObject, IClientProxy
{
public ClientProxy(IConnection connection, IHubPipelineInvoker invoker, string hubName, IList exclude);
public Task Invoke(string method, params object[] args);
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);
}
复制代码
复制代码
// 摘要:
// A server side proxy for the client side hub.
public interface IClientProxy
{
// 摘要:
// Invokes a method on the connection(s) represented by the Microsoft.AspNet.SignalR.Hubs.IClientProxy
// instance.
//
// 参数:
// method:
// name of the method to invoke
//
// args:
// argumetns to pass to the client
//
// 返回结果:
// A task that represents when the data has been sent to the client.
Task Invoke(string method, params object[] args);
}
}
复制代码
可以看到,运行时如果以IClientProxy注入,就一个Invoke方法。
好,挖到这儿,可以有一些思路了。
Clients所有的操作最终都是通过IClientProxy的Invoke来执行的。
如果让IChatClient通过某种方式和IClientProxy建立起非运行时的联系,就能实现强类型了。
这样的话