C# 实现AOP 的几种常见方式
来源:互联网 发布:mac优酷客户端 编辑:程序博客网 时间:2024/06/03 14:57
原文出处:http://www.cnblogs.com/zuowj/p/7501896.html
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的中统一处理业务逻辑的一种技术,比较常见的场景是:日志记录,错误捕获、性能监控等
AOP的本质是通过代理对象来间接执行真实对象,在代理类中往往会添加装饰一些额外的业务代码,比如如下代码:
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)
如下代码:
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实现方式,代码也还是比较简单的,效率相对第一种要慢一点,但对于普通的反射来说又高一些,代码实现如下:
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的远程访问对象时的直实代理类来实现,代码如下,这个可能相比以上两种稍微复杂一点:
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等。
以下是实现的全部代码,代码中我已进行了一些基本的优化,可以直接使用:
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
r
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
m
in
assy.GetModules(
false
))
{
foreach
(
var
t
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
m
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,并重写相应的方法,或在指定的方法上加上所需的过滤器特性,如下代码所示:
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;
}
}
最后前端实际调用就非常简单了,代码如下:
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代码执行效果如下:
- C# 实现AOP 的几种常见方式
- C# 实现AOP 的几种常见方式
- AOP的实现的几种方式
- Spring AOP的几种实现方式
- Spring AOP的几种实现方式
- 实现Spring AOP 应用的几种方式
- spring AOP的几种方式及实现原理分析
- spring aop advice注解实现的几种方式
- Spring中AOP的几种实现方式
- 关于Spring核心原理Aop的几种实现方式
- C#单例类的几种实现方式
- C# DataGridView绑定数据源的几种常见方式
- Fibonacci series(斐波纳契数列)的几种常见实现方式
- 几种常见的分页查询实现方式
- spring aop的几种配置方式:
- Spring AOP几种方式的使用
- C#实现文件下载的几种方式
- C#实现文件下载的几种方式
- ssh整合时遇到Error creating bean with name 'sessionFactory' defined in class
- [RK3288][Android6.0] 调试笔记 --- hwclock命令无法使用
- plsql12 格式化sql 语句
- Swift UIGestureRecognizer介绍
- MYCAT使用篇(1)
- C# 实现AOP 的几种常见方式
- React Native 网络请求封装:使用Promise封装fetch请求
- 抓老鼠啊~亏了还是赚了?
- 【题目】[USACO5.2]蜗牛的旅行Snail Trails
- ICMP洪水
- LDAP启动:slaptest: bad configuration file!
- postgis最短路径查询
- 产生随机数
- idea maven spring+springmvc+sql server+mybatis项目配置