接上一篇,文章末尾抛出了2个问题:

  1. 能不能让客户端声明一个强类型的方法列表呢?这样首先不容易写错。
  2. 同样的,能不能让服务端声明一个强类型的方法列表给客户端调用呢?

如果要让客户端的方法以强类型出现在服务端,同样的,服务端的方法也以强类型出现在客户端,那就必须声明类似契约一样的载体。比如:

  1. public interface IChatClient
  2. {
  3. void broadcast(string name, string message);
  4. }
  1. public interface IChatHub
  2. {
  3. void Send(string name, string message);
  4. }

分别建立ChatClient接口和ChatHub的接口。

  1. public class ChatHub : Hub<IChatClient>
  2. {
  3. ...
  4. }

这是最终的目标,一个泛型Hub。

好,现在需要进行一些分析,怎样才能让Hub支持泛型。

首先,看一下Hub是如何操作客户端方法的:

  1. Clients.AllExcept(Context.ConnectionId).broadcast(name, message);

Hub通过Clients来操作所有客户端的行为。那么这个Clients又是什么类型的呢?

  1. // 摘要:
  2. // Gets a dynamic object that represents all clients connected to this hub (not
  3. // hub instance).
  4. IHubCallerConnectionContext Clients { get; set; }

通过IHub接口看到,Clients的类型是IHubCallerConnectionContext,点进去看:

  1. // 摘要:
  2. // Encapsulates all information about an individual SignalR connection for an
  3. // Microsoft.AspNet.SignalR.Hubs.IHub.
  4. public interface IHubCallerConnectionContext : IHubConnectionContext
  5. {
  6. [Dynamic]
  7. dynamic Caller { get; }
  8. [Dynamic]
  9. dynamic Others { get; }
  10.  
  11. dynamic OthersInGroup(string groupName);
  12. dynamic OthersInGroups(IList<string> groupNames);
  13. }

IHubCallerConnectionContext又继承IHubConnectionContext,再点进去看:

  1. // 摘要:
  2. // Encapsulates all information about a SignalR connection for an Microsoft.AspNet.SignalR.Hubs.IHub.
  3. public interface IHubConnectionContext
  4. {
  5. [Dynamic]
  6. dynamic All { get; }
  7.  
  8. dynamic AllExcept(params string[] excludeConnectionIds);
  9. dynamic Client(string connectionId);
  10. dynamic Clients(IList<string> connectionIds);
  11. dynamic Group(string groupName, params string[] excludeConnectionIds);
  12. dynamic Groups(IList<string> groupNames, params string[] excludeConnectionIds);
  13. dynamic User(string userId);
  14. }

一目了然,所有Clients的操作方法都在这儿了,全是动态类型的,这也是为什么在Hub中写到Clients.All.xxx的时候已经是动态的了,那么运行时,这些操作都是什么类型的呢?试一下:

运行时,Clients的操作返回的是ClientProxy类型,从代码中扒出来:

  1. public class ClientProxy : DynamicObject, IClientProxy
  2. {
  3. public ClientProxy(IConnection connection, IHubPipelineInvoker invoker, string hubName, IList<string> exclude);
  4.  
  5. public Task Invoke(string method, params object[] args);
  6. public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);
  7. }
  1. // 摘要:
  2. // A server side proxy for the client side hub.
  3. public interface IClientProxy
  4. {
  5. // 摘要:
  6. // Invokes a method on the connection(s) represented by the Microsoft.AspNet.SignalR.Hubs.IClientProxy
  7. // instance.
  8. //
  9. // 参数:
  10. // method:
  11. // name of the method to invoke
  12. //
  13. // args:
  14. // argumetns to pass to the client
  15. //
  16. // 返回结果:
  17. // A task that represents when the data has been sent to the client.
  18. Task Invoke(string method, params object[] args);
  19. }
  20. }

可以看到,运行时如果以IClientProxy注入,就一个Invoke方法。

好,挖到这儿,可以有一些思路了。

  1. Clients所有的操作最终都是通过IClientProxy的Invoke来执行的。
  2. 如果让IChatClient通过某种方式和IClientProxy建立起非运行时的联系,就能实现强类型了。
  3. 这样的话,就需要有一个Hub<T>的类,然后把Clients里所有的操作在Hub<T>中重新实现一次。
  4. 然后T又是客户端的行为接口,因此,需要对Hub<T>进行静态扩展,让IClientProxy的Invoke方法能够被T的所有方法自动调用。

核心攻克点找到了,解决了4,就能一路解决1。怎样才能让IClientProxy的Invoke自动的被T的所有方法调用呢?AOP可以!可以用Castle对T进行动态织入。到这儿可以动手了,先建立一个Hub扩展类:

  1. public static class HubExtensions
  2. {
  3. static readonly ProxyGenerator generator = new ProxyGenerator();
  4.  
  5. public static T GetClientBehavior<T>(this IClientProxy clientProxy) where T : class
  6. {
  7. return (T)generator.CreateInterfaceProxyWithoutTarget<T>(new ClientBehaviorInterceptor(clientProxy));
  8. }
  9. }

让所有的IClientProxy执行GetClientBehavior方法,然后内部进行拦截器装载,并将IClientProxy塞进拦截器。

  1. public class ClientBehaviorInterceptor:IInterceptor
  2. {
  3. public ClientBehaviorInterceptor(IClientProxy clientProxy)
  4. {
  5. this.clientProxy = clientProxy;
  6. }
  7.  
  8. IClientProxy clientProxy;
  9.  
  10. public void Intercept(IInvocation invocation)
  11. {
  12. clientProxy.Invoke(invocation.Method.Name, invocation.Arguments);
  13. }
  14. }

拦截器中,每当T执行方法的时候,clientProxy就执行Invoke方法,把T的方法名和T的参数传入,这就达到了原先动态调用客户端方法传入参数并执行的效果。

然后就是写一个Hub<T>了。

  1. public abstract class Hub<T> : Hub where T : class
  2. {
  3. protected T All { get { return (Clients.All as IClientProxy).GetClientBehavior<T>(); } }
  4.  
  5. protected T Any(params string[] connectionIds)
  6. {
  7. return (Clients.Clients(connectionIds) as IClientProxy).GetClientBehavior<T>();
  8. }
  9.  
  10. protected T Except(params string[] connectionIds)
  11. {
  12. return (Clients.AllExcept(connectionIds) as IClientProxy).GetClientBehavior<T>();
  13. }
  14.  
  15. protected T Client(string connectionId)
  16. {
  17. return (Clients.Client(connectionId) as IClientProxy).GetClientBehavior<T>();
  18. }
  19.  
  20. protected T Caller { get { return (Clients.Caller as IClientProxy).GetClientBehavior<T>(); } }
  21. }

把Clients中所有的操作都在这儿写一遍,例子中就写了5个。通过刚才的扩展方法,返回的T已经是经过AOP的了。最后,把最初的ChatHub改一下:

让ChatHub继承Hub<T>,T为IChatClient,如图示,已经可以通过Except方法用强类型调用客户端方法了。执行一下看看:

到此,服务端改造结束。服务端已经可以接受强类型的客户端行为。

下一篇将对客户端部分进行强类型改造。

最后附上一个基于SignalR的聊天室玩具,绿色无毒:http://www.royarea.cn/chatroom

转载请注明出处:http://www.cnblogs.com/royding/p/3750412.html

 
 
 
标签: SignalRAOP

泛型Hub的更多相关文章

  1. SignalR循序渐进(二)泛型Hub

    接上一篇,文章末尾抛出了2个问题: 能不能让客户端声明一个强类型的方法列表呢?这样首先不容易写错. 同样的,能不能让服务端声明一个强类型的方法列表给客户端调用呢? 如果要让客户端的方法以强类型出现在服 ...

  2. Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参

    继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...

  3. SignalR 循序渐进

    SignalR 循序渐进(五)多个Hub服务器下的消息订阅 hellsoul86 2014-08-18 11:29 阅读:840 评论:7     SignalR 循序渐进(四) Hub的生命周期以及 ...

  4. SignalR循序渐进(三)简易的集群通讯组件

    上一篇演示了泛型Hub的实现,微软于6月17日更新了SignalR 2.1.0,然后自带了泛型Hub,于是就不需要自己去实现了…(微软你为啥不早一个月自带啊…).不过没关系,SignalR出彩之处不在 ...

  5. .Net Core SignalR 初体验

    前言 Asp.Net SignalR已经出来很久了,但是一直没有静下心来好好看看.昨天花了几个小时的时间看了下.首先借鉴了官方文档,如何搭建一个SignalR的Demo. 参考文章:https://d ...

  6. 一起学 Java(三) 集合框架、数据结构、泛型

    一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...

  7. .NET面试题系列[8] - 泛型

    “可变性是以一种类型安全的方式,将一个对象作为另一个对象来使用.“ - Jon Skeet .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] ...

  8. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  9. 编写高质量代码:改善Java程序的151个建议(第7章:泛型和反射___建议106~109)

    建议106:动态代理可以使代理模式更加灵活 Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发.我们知道一个静态代理是通过主题角色(Prox ...

随机推荐

  1. git stash用法

    使用场景: 当前修改的代码还不足以提交commit,但又必须切换到其他分支,要想完成这样的操作就可以使用git stash git stash意思就是备份当前的工作区的内容,从最近的一次提交中读取相关 ...

  2. Python 基于学习 网络小爬虫

    <span style="font-size:18px;"># # 百度贴吧图片网络小爬虫 # import re import urllib def getHtml( ...

  3. [LeetCode] 032. Longest Valid Parentheses (Hard) (C++)

    指数:[LeetCode] Leetcode 指标解释 (C++/Java/Python/Sql) Github: https://github.com/illuz/leetcode 032. Lon ...

  4. ES6 扫盲

    原文地址:ECMAScript 6 扫盲--小胡子 1. let.const 和 block 作用域 let 允许创建块级作用域,ES6 推荐在函数中使用 let 定义变量,而非 var: var a ...

  5. 于ios7在遇到一些发展deprecated问题

    cell.textLabel.textAlignment = UITextAlignmentCenter; 现在我想写cell.textLabel.textAlignment =NSTextAlign ...

  6. 平均得分 【杭州电-HDOJ-2023】 附加题+详细说明

    /* 平均得分 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  7. 杭州电acm理工大舞台版

    我要参加全国软件设计大赛C/C++学生语言组,前一个假设<C训练和演习,并总结手>没看完,请阅读上述并根据所作的训练,然后做下面的练习. 门户:http://blog.csdn.net/l ...

  8. 联合县城市,采用ajax,而使用ul模拟select下拉

    接待处代码 js //采用jquery展示鼠标放到省ul下拉显示 $("#province").hover(function(){                          ...

  9. MVC4的过滤器

    过滤器 提供的四种基本类型过滤器接口,IAuthorizationFilter.IActionFilter.IResultFilter和IExceptionFilter,可通过继承对应的接口和Filt ...

  10. .net EF 事物 订单流水号的生成 (二):观察者模式、事物、EF

    针对.net EF 事物 订单流水号的生成 (一)  的封装. 数据依然不变. using System; using System.Linq; using System.Transactions; ...