這篇文章主要介紹了如何利用C#實(shí)現(xiàn)AOP,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
為企業(yè)提供成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)站優(yōu)化、全網(wǎng)營(yíng)銷推廣、競(jìng)價(jià)托管、品牌運(yùn)營(yíng)等營(yíng)銷獲客服務(wù)。創(chuàng)新互聯(lián)擁有網(wǎng)絡(luò)營(yíng)銷運(yùn)營(yíng)團(tuán)隊(duì),以豐富的互聯(lián)網(wǎng)營(yíng)銷經(jīng)驗(yàn)助力企業(yè)精準(zhǔn)獲客,真正落地解決中小企業(yè)營(yíng)銷獲客難題,做到“讓獲客更簡(jiǎn)單”。自創(chuàng)立至今,成功用技術(shù)實(shí)力解決了企業(yè)“網(wǎng)站建設(shè)、網(wǎng)絡(luò)品牌塑造、網(wǎng)絡(luò)營(yíng)銷”三大難題,同時(shí)降低了營(yíng)銷成本,提高了有效客戶轉(zhuǎn)化率,獲得了眾多企業(yè)客戶的高度認(rèn)可!AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的中統(tǒng)一處理業(yè)務(wù)邏輯的一種技術(shù),比較常見的場(chǎng)景是:日志記錄,錯(cuò)誤捕獲、性能監(jiān)控等
AOP的本質(zhì)是通過代理對(duì)象來間接執(zhí)行真實(shí)對(duì)象,在代理類中往往會(huì)添加裝飾一些額外的業(yè)務(wù)代碼,比如如下代碼:
class RealA { public virtual string Pro { get; set; } public virtual void ShowHello(string name) { Console.WriteLine($"Hello!{name},Welcome!"); } } //調(diào)用: var a = new RealA(); a.Pro = "測(cè)試"; a.ShowHello("夢(mèng)在旅途");
這段代碼很簡(jiǎn)單,只是NEW一個(gè)對(duì)象,然后設(shè)置屬性及調(diào)用方法,但如果我想在設(shè)置屬性前后及調(diào)用方法前后或報(bào)錯(cuò)都能收集日志信息,該如何做呢?可能大家會(huì)想到,在設(shè)置屬性及調(diào)用方法前后都加上記錄日志的代碼不就可以了,雖然這樣是可以,但如果很多地方都要用到這個(gè)類的時(shí)候,那重復(fù)的代碼是否太多了一些吧,所以我們應(yīng)該使用代理模式或裝飾模式,將原有的真實(shí)類RealA委托給代理類ProxyRealA來執(zhí)行,代理類中在設(shè)置屬性及調(diào)用方法時(shí),再添加記錄日志的代碼就可以了,這樣可以保證代碼的干凈整潔,也便于代碼的后期維護(hù)。(注意,在C#中若需被子類重寫,父類必需是虛方法或虛屬性virtual)
如下代碼:
class ProxyRealA : RealA { public override string Pro { get { return base.Pro; } set { ShowLog("設(shè)置Pro屬性前日志信息"); base.Pro = value; ShowLog($"設(shè)置Pro屬性后日志信息:{value}"); } } public override void ShowHello(string name) { try { ShowLog("ShowHello執(zhí)行前日志信息"); base.ShowHello(name); ShowLog("ShowHello執(zhí)行后日志信息"); } catch(Exception ex) { ShowLog($"ShowHello執(zhí)行出錯(cuò)日志信息:{ex.Message}"); } } private void ShowLog(string log) { Console.WriteLine($"{DateTime.Now.ToString()}-{log}"); } } //調(diào)用: var aa = new ProxyRealA(); aa.Pro = "測(cè)試2"; aa.ShowHello("zuowenjun.cn");
這段代碼同樣很簡(jiǎn)單,就是ProxyRealA繼承自RealA類,即可看成是ProxyRealA代理RealA,由ProxyRealA提供各種屬性及方法調(diào)用。這樣在ProxyRealA類內(nèi)部屬性及方法執(zhí)行前后都有統(tǒng)一記錄日志的代碼,不論在哪里用這個(gè)RealA類,都可以直接用ProxyRealA類代替,因?yàn)槔锸咸鎿Q原則,父類可以被子類替換,而且后續(xù)若想更改日志記錄代碼方式,只需要在ProxyRealA中更改就行了,這樣所有用到的ProxyRealA類的日志都會(huì)改變,是不是很爽。
上述執(zhí)行結(jié)果如下圖示:
以上通過定義代理類的方式能夠?qū)崿F(xiàn)在方法中統(tǒng)一進(jìn)行各種執(zhí)行點(diǎn)的攔截代碼邏輯處理,攔截點(diǎn)(或者稱為:橫切面,切面點(diǎn))一般主要為:執(zhí)行前,執(zhí)行后,發(fā)生錯(cuò)誤,雖然解決了之前直接調(diào)用真實(shí)類RealA時(shí),需要重復(fù)增加各種邏輯代碼的問題,但隨之而來的新問題又來了,那就是當(dāng)一個(gè)系統(tǒng)中的類非常多的時(shí)候,如果我們針對(duì)每個(gè)類都定義一個(gè)代理類,那么系統(tǒng)的類的個(gè)數(shù)會(huì)成倍增加,而且不同的代理類中可能某些攔截業(yè)務(wù)邏輯代碼都是相同的,這種情況同樣是不能允許的,那有沒有什么好的辦法呢?答案是肯定的,以下是我結(jié)合網(wǎng)上資源及個(gè)人總結(jié)的如下幾種常見的實(shí)現(xiàn)AOP的方式,各位可以參考學(xué)習(xí)。
第一種:靜態(tài)織入,即:在編譯時(shí),就將各種涉及AOP攔截的代碼注入到符合一定規(guī)則的類中,編譯后的代碼與我們直接在RealA調(diào)用屬性或方法前后增加代碼是相同的,只是這個(gè)工作交由編譯器來完成。
PostSharp:PostSharp的Aspect是使用Attribute實(shí)現(xiàn)的,我們只需事先通過繼承自O(shè)nMethodBoundaryAspect,然后重寫幾個(gè)常見的方法即可,如:OnEntry,OnExit等,最后只需要在需要進(jìn)行AOP攔截的屬性或方法上加上AOP攔截特性類即可。由于PostSharp是靜態(tài)織入的,所以相比其它的通過反射或EMIT反射來說效率是最高的,但PostSharp是收費(fèi)版本的,而且網(wǎng)上的教程比較多,我就不在此重復(fù)說明了。
第二種:EMIT反射,即:通過Emit反射動(dòng)態(tài)生成代理類,如下Castle.DynamicProxy
的AOP實(shí)現(xiàn)方式,代碼也還是比較簡(jiǎn)單的,效率相對(duì)第一種要慢一點(diǎn),但對(duì)于普通的反射來說又高一些,代碼實(shí)現(xiàn)如下:
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(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 + "執(zhí)行前,入?yún)ⅲ? + string.Join(",", invocation.Arguments)); } protected override void PerformProceed(IInvocation invocation) { Console.WriteLine(invocation.Method.Name + "執(zhí)行中"); try { base.PerformProceed(invocation); } catch (Exception ex) { HandleException(ex); } } protected override void PostProceed(IInvocation invocation) { Console.WriteLine(invocation.Method.Name + "執(zhí)行后,返回值:" + 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!"); } } }
簡(jiǎn)要說明一下代碼原理,先創(chuàng)建ProxyGenerator類實(shí)例,從名字就看得出來,是代理類生成器,然后實(shí)例化一個(gè)基于繼承自StandardInterceptor的TestInterceptor,這個(gè)TestInterceptor是一個(gè)自定義的攔截器,最后通過generator.CreateClassProxy
動(dòng)態(tài)創(chuàng)建了一個(gè)繼承自TestA的動(dòng)態(tài)代理類,這個(gè)代理類只有在運(yùn)行時(shí)才會(huì)生成的,后面就可以如代碼所示,直接用動(dòng)態(tài)代理類對(duì)象實(shí)例Test操作TestA的所有屬性與方法,當(dāng)然這里需要注意,若需要被動(dòng)態(tài)代理類所代理并攔截,則父類的屬性或方法必需是virtual,這點(diǎn)與我上面說的直接寫一個(gè)代理類相同。
上述代碼運(yùn)行效果如下:
第三種:普通反射+利用Remoting的遠(yuǎn)程訪問對(duì)象時(shí)的直實(shí)代理類來實(shí)現(xiàn),代碼如下,這個(gè)可能相比以上兩種稍微復(fù)雜一點(diǎn):
以上代碼實(shí)現(xiàn)步驟說明:
1.這里定義的一個(gè)真實(shí)類AopClass必需繼承自ContextBoundObject類,而ContextBoundObject類又直接繼承自MarshalByRefObject類,表明該類是上下文綁定對(duì)象,允許在支持遠(yuǎn)程處理的應(yīng)用程序中跨應(yīng)用程序域邊界訪問對(duì)象,說白了就是可以獲取這個(gè)真實(shí)類的所有信息,以便可以被生成動(dòng)態(tài)代理。
2.定義繼承自ProxyAttribute的代理特性標(biāo)識(shí)類AopAttribute,以表明哪些類可以被代理,同時(shí)注意重寫CreateInstance方法,在CreateInstance方法里實(shí)現(xiàn)通過委托與生成透明代理類的過程,realProxy.GetTransparentProxy()
非常重要,目的就是根據(jù)定義的AopProxy代理類獲取生成透明代理類對(duì)象實(shí)例。
3.實(shí)現(xiàn)通用的AopProxy代理類,代理類必需繼承自RealProxy類,在這個(gè)代理類里面重寫Invoke方法,該方法是統(tǒng)一執(zhí)行被代理的真實(shí)類的所有方法、屬性、字段的出入口,我們只需要在該方法中根據(jù)傳入的IMessage進(jìn)行判斷并實(shí)現(xiàn)相應(yīng)的攔截代碼即可。
4.最后在需要進(jìn)行Aop攔截的類上標(biāo)注AopAttribute即可(注意:被標(biāo)識(shí)的類必需是如第1條說明的繼承自ContextBoundObject類),在實(shí)際調(diào)用的過程中是感知不到任何的變化。且AopAttribute可以被子類繼承,也就意味著所有子類都可以被代理并攔截。
如上代碼運(yùn)行效果如下:
這里順便分享微軟官方如果利用RealProxy類實(shí)現(xiàn)AOP的,詳見地址:https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
第四種:反射+ 通過定義統(tǒng)一的出入口,并運(yùn)用一些特性實(shí)現(xiàn)AOP的效果,比如:常見的MVC、WEB API中的過濾器特性 ,我這里根據(jù)MVC的思路,實(shí)現(xiàn)了類似的MVC過濾器的AOP效果,只是中間用到了反射,可能性能不佳,但效果還是成功實(shí)現(xiàn)了各種攔截,正如MVC一樣,既支持過濾器特性,也支持Controller中的Action執(zhí)行前,執(zhí)行后,錯(cuò)誤等方法實(shí)現(xiàn)攔截
實(shí)現(xiàn)思路如下:
A.過濾器及Controller特定方法攔截實(shí)現(xiàn)原理:
1.獲取程序集中所有繼承自Controller的類型;
2.根據(jù)Controller的名稱找到第1步中的對(duì)應(yīng)的Controller的類型:FindControllerType
3.根據(jù)找到的Controller類型及Action的名稱找到對(duì)應(yīng)的方法:FindAction
4.創(chuàng)建Controller類型的實(shí)例;
5.根據(jù)Action方法找到定義在方法上的所有過濾器特性(包含:執(zhí)行前、執(zhí)行后、錯(cuò)誤)
6.執(zhí)行Controller中的OnActionExecuting方法,隨后執(zhí)行執(zhí)行前的過濾器特性列表,如:ActionExecutingFilter
7.執(zhí)行Action方法,獲得結(jié)果;
8.執(zhí)行Controller中的OnActionExecuted方法,隨后執(zhí)行執(zhí)行后的過濾器特性列表,如:ActionExecutedFilter
9.通過try catch在catch中執(zhí)行Controller中的OnActionError方法,隨后執(zhí)行錯(cuò)誤過濾器特性列表,如:ActionErrorFilter
10.最后返回結(jié)果;
B.實(shí)現(xiàn)執(zhí)行路由配置效果原理:
1.增加可設(shè)置路由模板列表方法:AddExecRouteTemplate,在方法中驗(yàn)證controller、action,并獲取模板中的占位符數(shù)組,最后保存到類全局對(duì)象中routeTemplates;
2.增加根據(jù)執(zhí)行路由執(zhí)行對(duì)應(yīng)的Controller中的Action方法的效果:Run,在該方法中主要遍歷所有路由模板,然后與實(shí)行執(zhí)行的請(qǐng)求路由信息通過正則匹配,若匹配OK,并能正確找到Controller及Action,則說明正確,并最終統(tǒng)一調(diào)用:Process方法,執(zhí)行A中的所有步驟最終返回結(jié)果。
需要說明該模擬MVC方案并沒有實(shí)現(xiàn)Action方法參數(shù)的的綁定功能,因?yàn)镸odelBinding本身就是比較復(fù)雜的機(jī)制,所以這里只是為了搞清楚AOP的實(shí)現(xiàn)原理,故不作這方面的研究,大家如果有空可以實(shí)現(xiàn),最終實(shí)現(xiàn)MVC不僅是ASP.NET MVC,還可以是Console MVC,甚至是Winform MVC等。
以下是實(shí)現(xiàn)的全部代碼,代碼中我已進(jìn)行了一些基本的優(yōu)化,可以直接使用:
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中攔截發(fā)出的消息!-{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中攔截發(fā)出的消息!-{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中攔截發(fā)出的消息!-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}"); } } public class AppContext { private static readonly Type ControllerType = typeof(Controller); private static readonly DictionarymatchedControllerTypes = new Dictionary (); private static readonly Dictionary matchedControllerActions = new Dictionary (); private Dictionary routeTemplates = new Dictionary (); public void AddExecRouteTemplate(string execRouteTemplate) { if (!Regex.IsMatch(execRouteTemplate, "{controller}", RegexOptions.IgnoreCase)) { throw new ArgumentException("執(zhí)行路由模板不正確,缺少{controller}"); } if (!Regex.IsMatch(execRouteTemplate, "{action}", RegexOptions.IgnoreCase)) { throw new ArgumentException("執(zhí)行路由模板不正確,缺少{action}"); } string[] keys = Regex.Matches(execRouteTemplate, @"(?<={)\w+(?=})", RegexOptions.IgnoreCase).Cast ().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, @"{(? \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($"在已配置的路由模板列表中未找到與該執(zhí)行路由相匹配的路由信息:{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}及參數(shù)個(gè)數(shù):{args.Count()}相匹配的方法"); } var filters = action.GetCustomAttributes (true); List execBeforeFilters = new List (); List execAfterFilters = new List (); List exceptionFilters = new List (); 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; } }
使用前,先定義一個(gè)繼承自Controller的類,如:TestController,并重寫相應(yīng)的方法,或在指定的方法上加上所需的過濾器特性,如下代碼所示:
public class TestController : Controller { public override void OnActionExecuting(MethodInfo action) { Console.WriteLine($"{action.Name}執(zhí)行前,OnActionExecuting---{DateTime.Now.ToString()}"); } public override void OnActionExecuted(MethodInfo action) { Console.WriteLine($"{action.Name}執(zhí)行后,OnActionExecuted--{DateTime.Now.ToString()}"); } public override void OnActionError(MethodInfo action, Exception ex) { Console.WriteLine($"{action.Name}執(zhí)行,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("這是測(cè)試拋出的錯(cuò)誤信息!"); } [ActionExecutingFilter] [ActionExecutedFilter] public int Add(int a, int b) { return a + b; } }
最后前端實(shí)際調(diào)用就非常簡(jiǎn)單了,代碼如下:
class MVCProgram { static void Main(string[] args) { try { var appContext = new AppContext(); object rs = appContext.Process("Test", "HelloWorld", "夢(mèng)在旅途"); Console.WriteLine($"Process執(zhí)行的結(jié)果1:{rs}"); Console.WriteLine("=".PadRight(50, '=')); appContext.AddExecRouteTemplate("{controller}/{action}/{name}"); appContext.AddExecRouteTemplate("{action}/{controller}/{name}"); object result1 = appContext.Run("HelloWorld/Test/夢(mèng)在旅途-zuowenjun.cn"); Console.WriteLine($"執(zhí)行的結(jié)果1:{result1}"); Console.WriteLine("=".PadRight(50, '=')); object result2 = appContext.Run("Test/HelloWorld/夢(mèng)在旅途-zuowenjun.cn"); Console.WriteLine($"執(zhí)行的結(jié)果2:{result2}"); Console.WriteLine("=".PadRight(50, '=')); appContext.AddExecRouteTemplate("{action}/{controller}/{a}/"); object result3 = appContext.Run("Add/Test/500/20"); Console.WriteLine($"執(zhí)行的結(jié)果3:{result3}"); object result4 = appContext.Run("Test/TestError/夢(mèng)在旅途-zuowenjun.cn"); Console.WriteLine($"執(zhí)行的結(jié)果4:{result4}"); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"發(fā)生錯(cuò)誤:{ex.Message}"); Console.ResetColor(); } Console.ReadKey(); } }
可以看到,與ASP.NET MVC有點(diǎn)類似,只是ASP.NET MVC是通過URL訪問,而這里是通過AppContext.Run 執(zhí)行路由URL 或Process方法,直接指定Controller、Action、參數(shù)來執(zhí)行。
通過以上調(diào)用代碼可以看出路由配置還是比較靈活的,當(dāng)然參數(shù)配置除外。如果大家有更好的想法也可以在下方評(píng)論交流,謝謝!
MVC代碼執(zhí)行效果如下:
C#是一個(gè)簡(jiǎn)單、通用、面向?qū)ο蟮木幊陶Z(yǔ)言,它由微軟Microsoft開發(fā),繼承了C和C++強(qiáng)大功能,并且去掉了一些它們的復(fù)雜特性,C#綜合了VB簡(jiǎn)單的可視化操作和C++的高運(yùn)行效率,以其強(qiáng)大的操作能力、優(yōu)雅的語(yǔ)法風(fēng)格、創(chuàng)新的語(yǔ)言特性和便捷的面向組件編程從而成為.NET開發(fā)的選語(yǔ)言,但它不適用于編寫時(shí)間急迫或性能非常高的代碼,因?yàn)镃#缺乏性能極高的應(yīng)用程序所需要的關(guān)鍵功能。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“如何利用C#實(shí)現(xiàn)AOP”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)成都網(wǎng)站建設(shè)公司,關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、網(wǎng)站建設(shè)公司香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。