ABP框架中的事件總線功能介紹
事件總線
關(guān)于事件總線
ABP 中,為了方便進(jìn)程間通訊,給開發(fā)者提供了一個(gè)叫 事件總線
的功能,事件總線分為 本地事件總線
、分布式事件總線
,本篇文章講的是 本地事件總線
,系列教程中暫時(shí)不考慮講解 分布式事件總線
。
事件總線
需要使用 Volo.Abp.EventBus
庫,ABP 包中自帶,不需要額外引入。
事件總線是通過 訂閱-發(fā)布 形式使用的,某一方只需要按照格式推送事件,而不需要關(guān)注是誰接收了事件和如何處理事件。
你可以參考官方文檔:https://docs.abp.io/zh-Hans/abp/latest/Local-Event-Bus
為什么需要這個(gè)東西
首先列舉一下,你工作開發(fā)的項(xiàng)目中,編寫 控制器時(shí),是不是有這幾種代碼。
// 記錄日志 1 Task.Run(()=> { _apiLog.Info($"xxxxxxxx"); });
// 記錄日志 2 catch(Exception ex) { _apiLog.Error(ex); }
// 記錄日志 3 _apiLog.Info($"登陸信息:用戶 [{userName}({clientAdrress})]\);
筆者認(rèn)為,改善的上述問的方法之一是將函數(shù)的功能跟記錄日志分開,函數(shù)執(zhí)行任務(wù)時(shí),只需要把狀態(tài)和信息通過事件總線推送,而不需要了關(guān)注應(yīng)該如何處理這些內(nèi)容。
另外,還有當(dāng)函數(shù)執(zhí)行某些步驟時(shí),產(chǎn)生了事件,開發(fā)者喜歡 new Thread
一個(gè)新的線程去執(zhí)行別的任務(wù),或者 Task.Run
。
其實(shí),通過事件總線,我們更加好地隔離代碼,遵從 單一職責(zé)原則
。當(dāng)然還有很多方面值得使用事件總線,這里我們就不再扯淡了。
前面,我們編寫了全局異常攔截器,還有日志組件,這一篇我們將通過事件總線,將 Web 程序的一些部件組合起來。
事件總線創(chuàng)建過程
訂閱事件
創(chuàng)建一個(gè)服務(wù)來訂閱事件,當(dāng)程序中發(fā)生某種事件時(shí),此服務(wù)將被調(diào)用。
事件服務(wù)必須繼承 ILocalEventHandler<in TEvent>
接口,并實(shí)現(xiàn)以下函數(shù):
Task HandleEventAsync(TEvent eventData);
一個(gè)系統(tǒng)中,事件服務(wù)可以有多個(gè),每個(gè)服務(wù)的 TEvent
類型不能相同,因?yàn)?nbsp;TEvent
的類型是調(diào)用服務(wù)的標(biāo)識(shí)。當(dāng)發(fā)生 TEvent
事件后,系統(tǒng)通過 TEvent
去找到這個(gè)服務(wù)。
事件服務(wù)創(chuàng)建完畢后,需要加入到依賴注入中,你可以多繼承一個(gè) ITransientDependency
接口,然后統(tǒng)一掃描程序集加入到 依賴注入容器中(第三篇提到過)。
事件
即上面提到的 TEvent
。
假設(shè)有一個(gè)系統(tǒng)中所有的事件服務(wù)都放到一個(gè)容器中,發(fā)布者只能傳遞一個(gè)事件,而不能指定誰來提供響應(yīng)服務(wù)。
容器是通過 TEvent
來查找服務(wù)的。
事件就是一個(gè)模型類,也可以使用 int
或者 string
等簡(jiǎn)單類型(請(qǐng)不要用簡(jiǎn)單類型做事件),用于傳遞信息。
一般使用 Event
做后綴。
發(fā)布事件
如果需要發(fā)布一個(gè)事件,只需要注入 ILocalEventBus
即可。
private readonly ILocalEventBus _localEventBus; public MyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
然后發(fā)布事件:
await _localEventBus.PublishAsync( new TEvent { ... ... } );
全局異常加入事件總線功能
創(chuàng)建事件
在 AbpBase.Web
中,創(chuàng)建一個(gè) Handlers
目錄,再在 Handlers
目錄下,創(chuàng)建 HandlerEvents
目錄。
然后在 HandlerEvents
目錄,創(chuàng)建一個(gè) CustomerExceptionEvent.cs
文件。
CustomerExceptionEvent
作為一個(gè)異常事件,用于傳遞異常的信息,而不僅僅是將 Exception ex
記錄就了事。
其文件內(nèi)容如下:
using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace AbpBase.Application.Handlers.HandlerEvents { /// <summary> /// 全局異常推送事件 /// </summary> public class CustomerExceptionEvent { /// <summary> /// 只記錄異常 /// </summary> /// <param name="ex"></param> public CustomerExceptionEvent(Exception ex) { Exception = ex; } /// <summary> /// 此異常發(fā)生時(shí),用戶請(qǐng)求的路由地址 /// </summary> /// <param name="ex"></param> /// <param name="actionRoute"></param> public CustomerExceptionEvent(Exception ex, string actionRoute) { Exception = ex; Action = actionRoute; } /// <summary> /// 此異常發(fā)生在哪個(gè)類型的方法中 /// </summary> /// <param name="ex"></param> /// <param name="method"></param> public CustomerExceptionEvent(Exception ex, MethodBase method) { Exception = ex; MethodInfo = (MethodInfo)method; } /// <summary> /// 記錄異常信息 /// </summary> /// <param name="ex"></param> /// <param name="actionRoute"></param> /// <param name="method"></param> public CustomerExceptionEvent(Exception ex, string actionRoute, MethodBase method) { Exception = ex; Action = actionRoute; MethodInfo = (MethodInfo)method; } /// <summary> /// 當(dāng)前出現(xiàn)位置 /// <example> /// <code> /// MethodInfo = (MethodInfo)MethodBase.GetCurrentMethod(); /// </code> /// </example> /// </summary> public MethodInfo MethodInfo { get; private set; } /// <summary> /// 發(fā)生異常的 Action /// </summary> public string Action { get; private set; } /// <summary> /// 具體異常 /// </summary> public Exception Exception { get; private set; } } }
訂閱事件
訂閱事件,即將其定義為事件的響應(yīng)者、服務(wù)提供者。
當(dāng)異常發(fā)生后,異常的位置,推送異常信息,那么誰來處理這些信息呢?是訂閱者。
這里我們定義一個(gè)異常日志處理類,來處理程序推送的異常信息。
在 AbpBase.Web
項(xiàng)目的 Handlers
目錄中,添加一個(gè) CustomerExceptionHandler
類,繼承:
public class CustomerExceptionHandler : ILocalEventHandler<CustomerExceptionEvent>, ITransientDependency
服務(wù)要處理事件,必須繼承 ILocalEventHandler<T>
,而 ITransientDependency
是為了此服務(wù)可以可以自動(dòng)注入到容器中。
其文件內(nèi)容如下:
using AbpBase.Application.Handlers.HandlerEvents; using Serilog; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; namespace AbpBase.Application.Handlers { /// <summary> /// 全局異常記錄日志 /// </summary> public class CustomerExceptionHandler : ILocalEventHandler<CustomerExceptionEvent>, ITransientDependency { private readonly ILogger _ILogger; public CustomerExceptionHandler(ILogger logger) { _ILogger = logger; } public async Task HandleEventAsync(CustomerExceptionEvent eventData) { StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.AppendLine(); stringBuilder.Append("Action: "); stringBuilder.AppendLine(eventData.Action); if (eventData.MethodInfo != null) { stringBuilder.Append("Class-Method: "); stringBuilder.Append(eventData.MethodInfo?.DeclaringType.FullName); stringBuilder.AppendLine(eventData.MethodInfo?.Name); } stringBuilder.Append("Source: "); stringBuilder.AppendLine(eventData.Exception.Source); stringBuilder.Append("TargetSite: "); stringBuilder.AppendLine(eventData.Exception.TargetSite?.ToString()); stringBuilder.Append("InnerException: "); stringBuilder.AppendLine(eventData.Exception.InnerException?.ToString()); stringBuilder.Append("Message: "); stringBuilder.AppendLine(eventData.Exception.Message); stringBuilder.Append("HelpLink: "); stringBuilder.AppendLine(eventData.Exception.HelpLink); _ILogger.Fatal(stringBuilder.ToString()); await Task.CompletedTask; } } }
這樣寫,記錄的日志可以有很好的層次結(jié)構(gòu)。
發(fā)布事件
定義了事件的格式和定義服務(wù)來訂閱事件后,我們來創(chuàng)建一個(gè)發(fā)布者。
我們修改一下 WebGlobalExceptionFilter
。
增加依賴注入:
private readonly ILocalEventBus _localEventBus; public WebGlobalExceptionFilter(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
發(fā)布事件:
public async Task OnExceptionAsync(ExceptionContext context) { if (!context.ExceptionHandled) { await _localEventBus.PublishAsync(new CustomerExceptionEvent(context.Exception, context.ActionDescriptor?.DisplayName)); ... ...
測(cè)試
創(chuàng)建一個(gè) Action :
[HttpGet("/T4")] public string MyWebApi4() { int a = 1; int b = 0; int c = a / b; return c.ToString(); }
然后訪問 https://localhost:5001/T4 ,會(huì)發(fā)現(xiàn)請(qǐng)求后報(bào)錯(cuò)
在 AbpBase.Web
的 Logs
目錄中,打開 -Fatal.txt
文件。
可以看到:
2020-09-16 18:49:27.750 +08:00 [FTL] Action: ApbBase.HttpApi.Controllers.TestController.MyWebApi4 (ApbBase.HttpApi) Source: ApbBase.HttpApi TargetSite: System.String MyWebApi4() InnerException: Message: Attempted to divide by zero. HelpLink:
除了異常信息外,我們還可以很方便的知道異常發(fā)生在 TestController.MyWebApi4
這個(gè)位置。
記錄事件
如果在普通方法里面出現(xiàn)異常,我們這樣這樣記錄:
catch (Exception ex) { ... new CustomerExceptionEvent(ex, MethodBase.GetCurrentMethod()); ... }
MethodBase.GetCurrentMethod()
可以獲取當(dāng)前正在運(yùn)行的方法,獲得信息后將此參數(shù)傳遞給異常記錄服務(wù),會(huì)自動(dòng)解析出具體是哪個(gè)地方發(fā)生異常。
由于目前 Web 程序中還沒有編寫什么服務(wù),因此我們先結(jié)合到異常日志功能中,后面編寫服務(wù)時(shí),會(huì)再次用到事件總線。
完整代碼參考:https://github.com/whuanle/AbpBaseStruct/tree/master/src/4/AbpBase
源碼地址:https://github.com/whuanle/AbpBaseStruct
到此這篇關(guān)于ABP框架中的事件總線功能的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
asp.net gridview實(shí)現(xiàn)全選,反選與刪除記錄的操作代碼
asp.net gridview實(shí)現(xiàn)全選,反選與刪除記錄的操作代碼,需要的朋友可以參考下。2011-07-07Razor TagHelper實(shí)現(xiàn)Markdown轉(zhuǎn)HTML的方法
下面小編就為大家分享一篇Razor TagHelper實(shí)現(xiàn)Markdown轉(zhuǎn)HTML的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12Asp.net管理信息系統(tǒng)中數(shù)據(jù)統(tǒng)計(jì)功能的實(shí)現(xiàn)方法
這篇文章主要介紹了Asp.net管理信息系統(tǒng)中數(shù)據(jù)統(tǒng)計(jì)功能的實(shí)現(xiàn)方法,需要的朋友可以參考下2017-07-07.NET Core 2.0遷移小技巧之MemoryCache問題修復(fù)解決的方法
這篇文章主要給大家介紹了關(guān)于.NET Core 2.0遷移小技巧之MemoryCache問題修復(fù)解決的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08.net WINFORM的GDI雙緩沖的實(shí)現(xiàn)方法
下面小編就為大家分享一篇.net WINFORM的GDI雙緩沖的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12ashx介紹以及ashx文件與aspx文件之間的區(qū)別
這篇文章主要介紹了ashx以及ashx文件與aspx文件之間的區(qū)別。需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-12-12asp.net 驗(yàn)證字符串是否為純數(shù)字檢測(cè)函數(shù)
如何驗(yàn)證字符串是否為純數(shù)字2010-03-03