C# 实现AOP 的几种常见方式

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的中统一处理业务逻辑的一种技术,比较常见的场景是:日志记录,错误捕获、性能监控等

AOP的本质是通过代理对象来间接执行真实对象,在代理类中往往会添加装饰一些额外的业务代码,比如如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     class  RealA
     {
         public  virtual  string  Pro {  get set ; }
 
         public  virtual  void  ShowHello( string  name)
         {
             Console.WriteLine($ "Hello!{name},Welcome!" );
         }
     }
 
 
//调用:
 
             var  a =  new  RealA();
             a.Pro =  "测试" ;
             a.ShowHello( "梦在旅途" );

这段代码很简单,只是NEW一个对象,然后设置属性及调用方法,但如果我想在设置属性前后及调用方法前后或报错都能收集日志信息,该如何做呢?可能大家会想到,在设置属性及调用方法前后都加上记录日志的代码不就可以了,虽然这样是可以,但如果很多地方都要用到这个类的时候,那重复的代码是否太多了一些吧,所以我们应该使用代理模式或装饰模式,将原有的真实类RealA委托给代理类ProxyRealA来执行,代理类中在设置属性及调用方法时,再添加记录日志的代码就可以了,这样可以保证代码的干净整洁,也便于代码的后期维护。(注意,在C#中若需被子类重写,父类必需是虚方法或虚属性virtual)

如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
     class  ProxyRealA : RealA
     {
 
         public  override  string  Pro
         {
             get
             {
                 return  base .Pro;
             }
             set
             {
                 ShowLog( "设置Pro属性前日志信息" );
                 base .Pro = value;
                 ShowLog($ "设置Pro属性后日志信息:{value}" );
             }
         }
 
         public  override  void  ShowHello( string  name)
         {
             try
             {
                 ShowLog( "ShowHello执行前日志信息" );
                 base .ShowHello(name);
                 ShowLog( "ShowHello执行后日志信息" );
             }
             catch (Exception ex)
             {
                 ShowLog($ "ShowHello执行出错日志信息:{ex.Message}" );
             }
         }
 
         private  void  ShowLog( string  log)
         {
             Console.WriteLine($ "{DateTime.Now.ToString()}-{log}" );
         }
     }
 
 
//调用:
             var  aa =  new  ProxyRealA();
             aa.Pro =  "测试2" ;
             aa.ShowHello( "zuowenjun.cn" );

这段代码同样很简单,就是ProxyRealA继承自RealA类,即可看成是ProxyRealA代理RealA,由ProxyRealA提供各种属性及方法调用。这样在ProxyRealA类内部属性及方法执行前后都有统一记录日志的代码,不论在哪里用这个RealA类,都可以直接用ProxyRealA类代替,因为里氏替换原则,父类可以被子类替换,而且后续若想更改日志记录代码方式,只需要在ProxyRealA中更改就行了,这样所有用到的ProxyRealA类的日志都会改变,是不是很爽。上述执行结果如下图示:

 以上通过定义代理类的方式能够实现在方法中统一进行各种执行点的拦截代码逻辑处理,拦截点(或者称为:横切面,切面点)一般主要为:执行前,执行后,发生错误,虽然解决了之前直接调用真实类RealA时,需要重复增加各种逻辑代码的问题,但随之而来的新问题又来了,那就是当一个系统中的类非常多的时候,如果我们针对每个类都定义一个代理类,那么系统的类的个数会成倍增加,而且不同的代理类中可能某些拦截业务逻辑代码都是相同的,这种情况同样是不能允许的,那有没有什么好的办法呢?答案是肯定的,以下是我结合网上资源及个人总结的如下几种常见的实现AOP的方式,各位可以参考学习。

第一种:静态织入,即:在编译时,就将各种涉及AOP拦截的代码注入到符合一定规则的类中,编译后的代码与我们直接在RealA调用属性或方法前后增加代码是相同的,只是这个工作交由编译器来完成。

PostSharp:PostSharp的Aspect是使用Attribute实现的,我们只需事先通过继承自OnMethodBoundaryAspect,然后重写几个常见的方法即可,如:OnEntry,OnExit等,最后只需要在需要进行AOP拦截的属性或方法上加上AOP拦截特性类即可。由于PostSharp是静态织入的,所以相比其它的通过反射或EMIT反射来说效率是最高的,但PostSharp是收费版本的,而且网上的教程比较多,我就不在此重复说明了,大家可以参见:使用PostSharp在.NET平台上实现AOP

第二种:EMIT反射,即:通过Emit反射动态生成代理类,如下Castle.DynamicProxy的AOP实现方式,代码也还是比较简单的,效率相对第一种要慢一点,但对于普通的反射来说又高一些,代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using  Castle.Core.Interceptor;
using  Castle.DynamicProxy;
using  NLog;
using  NLog.Config;
using  NLog.Win32.Targets;
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Threading.Tasks;
 
namespace  ConsoleApp
{
     class  Program
     {
         static  void  Main( string [] args)
         {
             ProxyGenerator generator =  new  ProxyGenerator();
             var  test = generator.CreateClassProxy<TestA>( new  TestInterceptor());
             Console.WriteLine($ "GetResult:{test.GetResult(Console.ReadLine())}" );
             test.GetResult2( "test" );
             Console.ReadKey();
         }
     }
 
     public  class  TestInterceptor : StandardInterceptor
     {
         private  static  NLog.Logger logger;
 
         protected  override  void  PreProceed(IInvocation invocation)
         {
             Console.WriteLine(invocation.Method.Name +  "执行前,入参:"  string .Join( "," , invocation.Arguments));
         }
 
         protected  override  void  PerformProceed(IInvocation invocation)
         {
             Console.WriteLine(invocation.Method.Name +  "执行中" );
             try
             {
                 base .PerformProceed(invocation);
             }
             catch  (Exception ex)
             {
                 HandleException(ex);
             }
         }
 
         protected  override  void  PostProceed(IInvocation invocation)
         {
             Console.WriteLine(invocation.Method.Name +  "执行后,返回值:"  + invocation.ReturnValue);
         }
 
         private  void  HandleException(Exception ex)
         {
             if  (logger ==  null )
             {
                 LoggingConfiguration config =  new  LoggingConfiguration();
 
                 ColoredConsoleTarget consoleTarget =  new  ColoredConsoleTarget();
                 consoleTarget.Layout =  "${date:format=HH\\:MM\\:ss} ${logger} ${message}" ;
                 config.AddTarget( "console" , consoleTarget);
 
                 LoggingRule rule1 =  new  LoggingRule( "*" , LogLevel.Debug, consoleTarget);
                 config.LoggingRules.Add(rule1);
                 LogManager.Configuration = config;
 
                 logger = LogManager.GetCurrentClassLogger();  //new NLog.LogFactory().GetCurrentClassLogger();
             }
             logger.ErrorException( "error" ,ex);
         }
     }
 
     public  class  TestA
     {
         public  virtual  string  GetResult( string  msg)
         {
             string  str = $ "{DateTime.Now.ToString(" yyyy-mm-dd HH:mm:ss ")}---{msg}" ;
             return  str;
         }
 
         public  virtual  string  GetResult2( string  msg)
         {
             throw  new  Exception( "throw Exception!" );
         }
     }
}

简要说明一下代码原理,先创建ProxyGenerator类实例,从名字就看得出来,是代理类生成器,然后实例化一个基于继承自StandardInterceptor的TestInterceptor,这个TestInterceptor是一个自定义的拦截器,最后通过generator.CreateClassProxy<TestA>(new TestInterceptor())动态创建了一个继承自TestA的动态代理类,这个代理类只有在运行时才会生成的,后面就可以如代码所示,直接用动态代理类对象实例Test操作TestA的所有属性与方法,当然这里需要注意,若需要被动态代理类所代理并拦截,则父类的属性或方法必需是virtual,这点与我上面说的直接写一个代理类相同。

上述代码运行效果如下:

 第三种:普通反射+利用Remoting的远程访问对象时的直实代理类来实现,代码如下,这个可能相比以上两种稍微复杂一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Remoting.Activation;
using  System.Runtime.Remoting.Messaging;
using  System.Runtime.Remoting.Proxies;
using  System.Text;
using  System.Threading.Tasks;
 
namespace  ConsoleApp
{
     class  Program
     {
         static  void  Main( string [] args)
         {
 
             var  A =  new  AopClass();
             A.Hello();
 
             var  aop =  new  AopClassSub( "梦在旅途" );
             aop.Pro =  "test" ;
             aop.Output( "hlf" );
             aop.ShowMsg();
             Console.ReadKey();
 
         }
     }
 
 
     [AopAttribute]
     public  class  AopClass : ContextBoundObject
     {
         public  string  Hello()
         {
             return  "welcome" ;
         }
 
     }
 
 
     public  class  AopClassSub : AopClass
     {
         public  string  Pro =  null ;
         private  string  Msg =  null ;
 
         public  AopClassSub( string  msg)
         {
             Msg = msg;
         }
 
         public  void  Output( string  name)
         {
             Console.WriteLine(name +  ",你好!-->P:"  + Pro);
         }
 
         public  void  ShowMsg()
         {
             Console.WriteLine($ "构造函数传的Msg参数内容是:{Msg}" );
         }
     }
 
 
 
     public  class  AopAttribute : ProxyAttribute
     {
         public  override  MarshalByRefObject CreateInstance(Type serverType)
         {
             AopProxy realProxy =  new  AopProxy(serverType);
             return  realProxy.GetTransparentProxy()  as  MarshalByRefObject;
         }
     }
 
     public  class  AopProxy : RealProxy
     {
         public  AopProxy(Type serverType)
             base (serverType) { }
 
         public  override  IMessage Invoke(IMessage msg)
         {
             if  (msg  is  IConstructionCallMessage)
             {
                 IConstructionCallMessage constructCallMsg = msg  as  IConstructionCallMessage;
                 IConstructionReturnMessage constructionReturnMessage =  this .InitializeServerObject((IConstructionCallMessage)msg);
                 RealProxy.SetStubData( this , constructionReturnMessage.ReturnValue);
                 Console.WriteLine( "Call constructor" );
                 return  constructionReturnMessage;
             }
             else
             {
                 IMethodCallMessage callMsg = msg  as  IMethodCallMessage;
                 IMessage message;
                 try
                 {
                     Console.WriteLine(callMsg.MethodName +  "执行前。。。" );
                     object [] args = callMsg.Args;
                     object  o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args);
                     Console.WriteLine(callMsg.MethodName +  "执行后。。。" );
                     message =  new  ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg);
                 }
                 catch  (Exception e)
                 {
                     message =  new  ReturnMessage(e, callMsg);
                 }
                 Console.WriteLine(message.Properties[ "__Return" ]);
                 return  message;
             }
         }
     }
 
}

以上代码实现步骤说明:

1.这里定义的一个真实类AopClass必需继承自ContextBoundObject类,而ContextBoundObject类又直接继承自MarshalByRefObject类,表明该类是上下文绑定对象,允许在支持远程处理的应用程序中跨应用程序域边界访问对象,说白了就是可以获取这个真实类的所有信息,以便可以被生成动态代理。

2.定义继承自ProxyAttribute的代理特性标识类AopAttribute,以表明哪些类可以被代理,同时注意重写CreateInstance方法,在CreateInstance方法里实现通过委托与生成透明代理类的过程,realProxy.GetTransparentProxy() 非常重要,目的就是根据定义的AopProxy代理类获取生成透明代理类对象实例。

3.实现通用的AopProxy代理类,代理类必需继承自RealProxy类,在这个代理类里面重写Invoke方法,该方法是统一执行被代理的真实类的所有方法、属性、字段的出入口,我们只需要在该方法中根据传入的IMessage进行判断并实现相应的拦截代码即可。

4.最后在需要进行Aop拦截的类上标注AopAttribute即可(注意:被标识的类必需是如第1条说明的继承自ContextBoundObject类),在实际调用的过程中是感知不到任何的变化。且AopAttribute可以被子类继承,也就意味着所有子类都可以被代理并拦截。

如上代码运行效果如下:

这里顺便分享微软官方如果利用RealProxy类实现AOP的,详见地址:https://msdn.microsoft.com/zh-cn/library/dn574804.aspx

 第四种:反射+ 通过定义统一的出入口,并运用一些特性实现AOP的效果,比如:常见的MVC、WEB API中的过滤器特性 ,我这里根据MVC的思路,实现了类似的MVC过滤器的AOP效果,只是中间用到了反射,可能性能不佳,但效果还是成功实现了各种拦截,正如MVC一样,既支持过滤器特性,也支持Controller中的Action执行前,执行后,错误等方法实现拦截

 实现思路如下:

A.过滤器及Controller特定方法拦截实现原理:

1.获取程序集中所有继承自Controller的类型; 

2.根据Controller的名称找到第1步中的对应的Controller的类型:FindControllerType

3.根据找到的Controller类型及Action的名称找到对应的方法:FindAction

4.创建Controller类型的实例;

5.根据Action方法找到定义在方法上的所有过滤器特性(包含:执行前、执行后、错误)

6.执行Controller中的OnActionExecuting方法,随后执行执行前的过滤器特性列表,如:ActionExecutingFilter

7.执行Action方法,获得结果;

8.执行Controller中的OnActionExecuted方法,随后执行执行后的过滤器特性列表,如:ActionExecutedFilter

9.通过try catch在catch中执行Controller中的OnActionError方法,随后执行错误过滤器特性列表,如:ActionErrorFilter

10.最后返回结果;

B.实现执行路由配置效果原理:

1.增加可设置路由模板列表方法:AddExecRouteTemplate,在方法中验证controller、action,并获取模板中的占位符数组,最后保存到类全局对象中routeTemplates; 

2.增加根据执行路由执行对应的Controller中的Action方法的效果: Run,在该方法中主要遍历所有路由模板,然后与实行执行的请求路由信息通过正则匹配,若匹配OK,并能正确找到Controller及Action,则说明正确,并最终统一调用:Process方法,执行A中的所有步骤最终返回结果。

需要说明该模拟MVC方案并没有实现Action方法参数的的绑定功能,因为ModelBinding本身就是比较复杂的机制,所以这里只是为了搞清楚AOP的实现原理,故不作这方面的研究,大家如果有空可以实现,最终实现MVC不仅是ASP.NET MVC,还可以是 Console MVC,甚至是Winform MVC等。

以下是实现的全部代码,代码中我已进行了一些基本的优化,可以直接使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
public  abstract  class  Controller
{
     public  virtual  void  OnActionExecuting(MethodInfo action)
     {
 
     }
 
     public  virtual  void  OnActionExecuted(MethodInfo action)
     {
 
     }
 
     public  virtual  void  OnActionError(MethodInfo action, Exception ex)
     {
 
     }
 
}
 
public  abstract  class  FilterAttribute : Attribute
{
     public  abstract  string  FilterType {  get ; }
     public  abstract  void  Execute(Controller ctrller,  object  extData);
}
 
public  class  ActionExecutingFilter : FilterAttribute
{
     public  override  string  FilterType =>  "BEFORE" ;
 
     public  override  void  Execute(Controller ctrller,  object  extData)
     {
         Console.WriteLine($ "我是在{ctrller.GetType().Name}.ActionExecutingFilter中拦截发出的消息!-{DateTime.Now.ToString()}" );
     }
}
 
public  class  ActionExecutedFilter : FilterAttribute
{
     public  override  string  FilterType =>  "AFTER" ;
 
     public  override  void  Execute(Controller ctrller,  object  extData)
     {
         Console.WriteLine($ "我是在{ctrller.GetType().Name}.ActionExecutedFilter中拦截发出的消息!-{DateTime.Now.ToString()}" );
     }
}
 
public  class  ActionErrorFilter : FilterAttribute
{
     public  override  string  FilterType =>  "EXCEPTION" ;
 
     public  override  void  Execute(Controller ctrller,  object  extData)
     {
         Console.WriteLine($ "我是在{ctrller.GetType().Name}.ActionErrorFilter中拦截发出的消息!-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}" );
     }
}
 
public  class  AppContext
{
     private  static  readonly  Type ControllerType =  typeof (Controller);
     private  static  readonly  Dictionary< string , Type> matchedControllerTypes =  new  Dictionary< string , Type>();
     private  static  readonly  Dictionary< string , MethodInfo> matchedControllerActions =  new  Dictionary< string , MethodInfo>();
     private  Dictionary< string , string []> routeTemplates =  new  Dictionary< string string []>();
 
 
     public  void  AddExecRouteTemplate( string  execRouteTemplate)
     {
         if  (!Regex.IsMatch(execRouteTemplate,  "{controller}" , RegexOptions.IgnoreCase))
         {
             throw  new  ArgumentException( "执行路由模板不正确,缺少{controller}" );
         }
 
         if  (!Regex.IsMatch(execRouteTemplate,  "{action}" , RegexOptions.IgnoreCase))
         {
             throw  new  ArgumentException( "执行路由模板不正确,缺少{action}" );
         }
 
         string [] keys = Regex.Matches(execRouteTemplate,  @"(?<={)\w+(?=})" , RegexOptions.IgnoreCase).Cast<Match>().Select(c => c.Value.ToLower()).ToArray();
 
         routeTemplates.Add(execRouteTemplate,keys);
     }
 
     public  object  Run( string  execRoute)
     {
         //{controller}/{action}/{id}
         string  ctrller =  null ;
         string  actionName =  null ;
         ArrayList args =  null ;
         Type controllerType =  null ;
         bool  findResult =  false ;
 
         foreach  ( var  in  routeTemplates)
         {
             string [] keys = r.Value;
             string  execRoutePattern = Regex.Replace(r.Key,  @"{(?<key>\w+)}" , (m) =>  string .Format( @"(?<{0}>.[^/\\]+)" , m.Groups[ "key" ].Value.ToLower()), RegexOptions.IgnoreCase);
 
             args =  new  ArrayList();
             if  (Regex.IsMatch(execRoute, execRoutePattern))
             {
                 var  match = Regex.Match(execRoute, execRoutePattern);
                 for  ( int  i = 0; i < keys.Length; i++)
                 {
                     if  ( "controller" .Equals(keys[i], StringComparison.OrdinalIgnoreCase))
                     {
                         ctrller = match.Groups[ "controller" ].Value;
                     }
                     else  if  ( "action" .Equals(keys[i], StringComparison.OrdinalIgnoreCase))
                     {
                         actionName = match.Groups[ "action" ].Value;
                     }
                     else
                     {
                         args.Add(match.Groups[keys[i]].Value);
                     }
                 }
 
                 if  ((controllerType = FindControllerType(ctrller)) !=  null  && FindAction(controllerType, actionName, args.ToArray()) !=  null )
                 {
                     findResult =  true ;
                     break ;
                 }
             }
         }
 
         if  (findResult)
         {
             return  Process(ctrller, actionName, args.ToArray());
         }
         else
         {
             throw  new  Exception($ "在已配置的路由模板列表中未找到与该执行路由相匹配的路由信息:{execRoute}" );
         }
     }
 
     public  object  Process( string  ctrller,  string  actionName,  params  object [] args)
     {
         Type matchedControllerType = FindControllerType(ctrller);
 
         if  (matchedControllerType ==  null )
         {
             throw  new  ArgumentException($ "未找到类型为{ctrller}的Controller类型" );
         }
 
         object  execResult =  null ;
         if  (matchedControllerType !=  null )
         {
             var  matchedController = (Controller)Activator.CreateInstance(matchedControllerType);
             MethodInfo action = FindAction(matchedControllerType, actionName, args);
             if  (action ==  null )
             {
                 throw  new  ArgumentException($ "在{matchedControllerType.FullName}中未找到与方法名:{actionName}及参数个数:{args.Count()}相匹配的方法" );
             }
 
 
             var  filters = action.GetCustomAttributes<FilterAttribute>( true );
             List<FilterAttribute> execBeforeFilters =  new  List<FilterAttribute>();
             List<FilterAttribute> execAfterFilters =  new  List<FilterAttribute>();
             List<FilterAttribute> exceptionFilters =  new  List<FilterAttribute>();
 
             if  (filters !=  null  && filters.Count() > 0)
             {
                 execBeforeFilters = filters.Where(f => f.FilterType ==  "BEFORE" ).ToList();
                 execAfterFilters = filters.Where(f => f.FilterType ==  "AFTER" ).ToList();
                 exceptionFilters = filters.Where(f => f.FilterType ==  "EXCEPTION" ).ToList();
             }
 
             try
             {
                 matchedController.OnActionExecuting(action);
 
                 if  (execBeforeFilters !=  null  && execBeforeFilters.Count > 0)
                 {
                     execBeforeFilters.ForEach(f => f.Execute(matchedController,  null ));
                 }
 
                 var  mParams = action.GetParameters();
                 object [] newArgs =  new  object [args.Length];
                 for  ( int  i = 0; i < mParams.Length; i++)
                 {
                     newArgs[i] = Convert.ChangeType(args[i], mParams[i].ParameterType);
                 }
 
                 execResult = action.Invoke(matchedController, newArgs);
 
                 matchedController.OnActionExecuted(action);
 
                 if  (execBeforeFilters !=  null  && execBeforeFilters.Count > 0)
                 {
                     execAfterFilters.ForEach(f => f.Execute(matchedController,  null ));
                 }
 
             }
             catch  (Exception ex)
             {
                 matchedController.OnActionError(action, ex);
 
                 if  (exceptionFilters !=  null  && exceptionFilters.Count > 0)
                 {
                     exceptionFilters.ForEach(f => f.Execute(matchedController, ex));
                 }
             }
 
 
         }
 
         return  execResult;
 
     }
 
     private  Type FindControllerType( string  ctrller)
     {
         Type matchedControllerType =  null ;
         if  (!matchedControllerTypes.ContainsKey(ctrller))
         {
             var  assy = Assembly.GetAssembly( typeof (Controller));
 
             foreach  ( var  in  assy.GetModules( false ))
             {
                 foreach  ( var  in  m.GetTypes())
                 {
                     if  (ControllerType.IsAssignableFrom(t) && !t.IsAbstract)
                     {
                         if  (t.Name.Equals(ctrller, StringComparison.OrdinalIgnoreCase) || t.Name.Equals($ "{ctrller}Controller" , StringComparison.OrdinalIgnoreCase))
                         {
                             matchedControllerType = t;
                             matchedControllerTypes[ctrller] = matchedControllerType;
                             break ;
                         }
                     }
                 }
             }
         }
         else
         {
             matchedControllerType = matchedControllerTypes[ctrller];
         }
 
         return  matchedControllerType;
     }
 
     private  MethodInfo FindAction(Type matchedControllerType,  string  actionName,  object [] args)
     {
         string  ctrlerWithActionKey = $ "{matchedControllerType.FullName}.{actionName}" ;
         MethodInfo action =  null ;
         if  (!matchedControllerActions.ContainsKey(ctrlerWithActionKey))
         {
             if  (args ==  null ) args =  new  object [0];
             foreach  ( var  in  matchedControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
             {
                 if  (m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == args.Length)
                 {
                     action = m;
                     matchedControllerActions[ctrlerWithActionKey] = action;
                     break ;
                 }
             }
         }
         else
         {
             action = matchedControllerActions[ctrlerWithActionKey];
         }
 
         return  action;
     }
}

使用前,先定义一个继承自Controller的类,如:TestController,并重写相应的方法,或在指定的方法上加上所需的过滤器特性,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public  class  TestController : Controller
{
     public  override  void  OnActionExecuting(MethodInfo action)
     {
         Console.WriteLine($ "{action.Name}执行前,OnActionExecuting---{DateTime.Now.ToString()}" );
     }
 
     public  override  void  OnActionExecuted(MethodInfo action)
     {
         Console.WriteLine($ "{action.Name}执行后,OnActionExecuted--{DateTime.Now.ToString()}" );
     }
 
     public  override  void  OnActionError(MethodInfo action, Exception ex)
     {
         Console.WriteLine($ "{action.Name}执行,OnActionError--{DateTime.Now.ToString()}:{ex.Message}" );
     }
 
     [ActionExecutingFilter]
     [ActionExecutedFilter]
     public  string  HelloWorld( string  name)
     {
         return  ($ "Hello World!->{name}" );
     }
 
     [ActionExecutingFilter]
     [ActionExecutedFilter]
     [ActionErrorFilter]
     public  string  TestError( string  name)
     {
         throw  new  Exception( "这是测试抛出的错误信息!" );
     }
 
     [ActionExecutingFilter]
     [ActionExecutedFilter]
     public  int  Add( int  a,  int  b)
     {
         return  a + b;
     }
}

最后前端实际调用就非常简单了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class  MVCProgram
{
     static  void  Main( string [] args)
     {
         try
         {
             var  appContext =  new  AppContext();
             object  rs = appContext.Process( "Test" "HelloWorld" "梦在旅途" );
             Console.WriteLine($ "Process执行的结果1:{rs}" );
 
             Console.WriteLine( "=" .PadRight(50,  '=' ));
 
             appContext.AddExecRouteTemplate( "{controller}/{action}/{name}" );
             appContext.AddExecRouteTemplate( "{action}/{controller}/{name}" );
 
             object  result1 = appContext.Run( "HelloWorld/Test/梦在旅途-zuowenjun.cn" );
             Console.WriteLine($ "执行的结果1:{result1}" );
 
             Console.WriteLine( "=" .PadRight(50,  '=' ));
 
             object  result2 = appContext.Run( "Test/HelloWorld/梦在旅途-zuowenjun.cn" );
             Console.WriteLine($ "执行的结果2:{result2}" );
 
             Console.WriteLine( "=" .PadRight(50,  '=' ));
 
             appContext.AddExecRouteTemplate( "{action}/{controller}/{a}/{b}" );
             object  result3 = appContext.Run( "Add/Test/500/20" );
             Console.WriteLine($ "执行的结果3:{result3}" );
 
             object  result4 = appContext.Run( "Test/TestError/梦在旅途-zuowenjun.cn" );
             Console.WriteLine($ "执行的结果4:{result4}" );
         }
         catch  (Exception ex)
         {
             Console.ForegroundColor = ConsoleColor.Red;
             Console.WriteLine($ "发生错误:{ex.Message}" );
             Console.ResetColor();
         }
 
         Console.ReadKey();
     }
}

可以看到,与ASP.NET MVC有点类似,只是ASP.NET MVC是通过URL访问,而这里是通过AppContext.Run 执行路由URL 或Process方法,直接指定Controller、Action、参数来执行。

通过以上调用代码可以看出路由配置还是比较灵活的,当然参数配置除外。如果大家有更好的想法也可以在下方评论交流,谢谢!

 MVC代码执行效果如下:


本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/7501896.html   ,如需转载请自行联系原作者
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
监控 前端开发 C#
C#进阶系列——AOP?AOP!
原文:C#进阶系列——AOP?AOP! 前言:今天大阅兵,可是苦逼的博主还得坐在电脑前写博客,为了弄清楚AOP,博主也是拼了。这篇打算写写AOP,说起AOP,其实博主接触这个概念也才几个月,了解后才知道,原来之前自己写的好多代码原理就是基于AOP的,比如MVC的过滤器Filter,它里面的异常捕捉可以通过FilterAttribute,IExceptionFilter去处理,这两个对象的处理机制内部原理应该就是AOP,只不过之前没有这个概念罢了。
1326 1
|
C#
【我们一起写框架】C#的AOP框架
原文:【我们一起写框架】C#的AOP框架 前言 AOP,大家都是听过的,它是一种面向切面的设计模式。 不过AOP虽然是被称为设计模式,但我们应该很少能看到AOP设计的框架。为什么呢? 因为,AOP单独设计的框架几乎是无法使用的。
908 0
|
监控 前端开发 C#