日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

ABP框架中的事件總線功能介紹_實用技巧

作者:癡者工良 ? 更新時間: 2022-04-27 編程語言

事件總線

關于事件總線

ABP 中,為了方便進程間通訊,給開發者提供了一個叫?事件總線?的功能,事件總線分為?本地事件總線分布式事件總線,本篇文章講的是?本地事件總線,系列教程中暫時不考慮講解?分布式事件總線

事件總線?需要使用?Volo.Abp.EventBus?庫,ABP 包中自帶,不需要額外引入。

事件總線是通過 訂閱-發布 形式使用的,某一方只需要按照格式推送事件,而不需要關注是誰接收了事件和如何處理事件。

你可以參考官方文檔:https://docs.abp.io/zh-Hans/abp/latest/Local-Event-Bus

為什么需要這個東西

首先列舉一下,你工作開發的項目中,編寫 控制器時,是不是有這幾種代碼。

// 記錄日志 1
Task.Run(()=>
{
	_apiLog.Info($"xxxxxxxx");
});
// 記錄日志 2
catch(Exception ex)
{
	_apiLog.Error(ex);
}
// 記錄日志 3
_apiLog.Info($"登陸信息:用戶 [{userName}({clientAdrress})]\);

筆者認為,改善的上述問的方法之一是將函數的功能跟記錄日志分開,函數執行任務時,只需要把狀態和信息通過事件總線推送,而不需要了關注應該如何處理這些內容。

另外,還有當函數執行某些步驟時,產生了事件,開發者喜歡?new Thread?一個新的線程去執行別的任務,或者?Task.Run

其實,通過事件總線,我們更加好地隔離代碼,遵從?單一職責原則?。當然還有很多方面值得使用事件總線,這里我們就不再扯淡了。

前面,我們編寫了全局異常攔截器,還有日志組件,這一篇我們將通過事件總線,將 Web 程序的一些部件組合起來。

事件總線創建過程

訂閱事件

創建一個服務來訂閱事件,當程序中發生某種事件時,此服務將被調用。

事件服務必須繼承?ILocalEventHandler?接口,并實現以下函數:

Task HandleEventAsync(TEvent eventData);

一個系統中,事件服務可以有多個,每個服務的?TEvent?類型不能相同,因為?TEvent?的類型是調用服務的標識。當發生?TEvent?事件后,系統通過?TEvent?去找到這個服務。

事件服務創建完畢后,需要加入到依賴注入中,你可以多繼承一個?ITransientDependency?接口,然后統一掃描程序集加入到 依賴注入容器中(第三篇提到過)。

事件

即上面提到的?TEvent

假設有一個系統中所有的事件服務都放到一個容器中,發布者只能傳遞一個事件,而不能指定誰來提供響應服務。

容器是通過?TEvent?來查找服務的。

事件就是一個模型類,也可以使用?int或者?string?等簡單類型(請不要用簡單類型做事件),用于傳遞信息。

一般使用?Event?做后綴。

發布事件

如果需要發布一個事件,只需要注入?ILocalEventBus?即可。

        private readonly ILocalEventBus _localEventBus;

        public MyService(ILocalEventBus localEventBus)
        {
            _localEventBus = localEventBus;
        }

然后發布事件:

            await _localEventBus.PublishAsync(
                new TEvent
                {
					... ...
                }
            );

全局異常加入事件總線功能

創建事件

在?AbpBase.Web?中,創建一個?Handlers?目錄,再在?Handlers?目錄下,創建?HandlerEvents?目錄。

然后在?HandlerEvents?目錄,創建一個?CustomerExceptionEvent.cs?文件。

CustomerExceptionEvent?作為一個異常事件,用于傳遞異常的信息,而不僅僅是將?Exception ex?記錄就了事。

其文件內容如下:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace AbpBase.Application.Handlers.HandlerEvents
{
    /// 
    /// 全局異常推送事件
    /// 
    public class CustomerExceptionEvent
    {
        /// 
        /// 只記錄異常
        /// 
        /// 
        public CustomerExceptionEvent(Exception ex)
        {
            Exception = ex;
        }

        /// 
        /// 此異常發生時,用戶請求的路由地址
        /// 
        /// 
        /// 
        public CustomerExceptionEvent(Exception ex, string actionRoute)
        {
            Exception = ex;
            Action = actionRoute;
        }

        /// 
        /// 此異常發生在哪個類型的方法中
        /// 
        /// 
        /// 
        public CustomerExceptionEvent(Exception ex, MethodBase method)
        {
            Exception = ex;
            MethodInfo = (MethodInfo)method;
        }

        /// 
        /// 記錄異常信息
        /// 
        /// 
        /// 
        /// 
        public CustomerExceptionEvent(Exception ex, string actionRoute, MethodBase method)
        {
            Exception = ex;
            Action = actionRoute;
            MethodInfo = (MethodInfo)method;
        }

        /// 
        /// 當前出現位置
        /// 
        /// 
        /// MethodInfo = (MethodInfo)MethodBase.GetCurrentMethod();
        /// 
        /// 
        /// 
        public MethodInfo MethodInfo { get; private set; }

        /// 
        /// 發生異常的 Action
        /// 
        public string Action { get; private set; }

        /// 
        /// 具體異常
        /// 
        public Exception Exception { get; private set; }
    }
}

訂閱事件

訂閱事件,即將其定義為事件的響應者、服務提供者。

當異常發生后,異常的位置,推送異常信息,那么誰來處理這些信息呢?是訂閱者。

這里我們定義一個異常日志處理類,來處理程序推送的異常信息。

在?AbpBase.Web?項目的?Handlers?目錄中,添加一個?CustomerExceptionHandler?類,繼承:

public class CustomerExceptionHandler : ILocalEventHandler, ITransientDependency

服務要處理事件,必須繼承?ILocalEventHandler,而?ITransientDependency?是為了此服務可以可以自動注入到容器中。

其文件內容如下:

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
{
    /// 
    /// 全局異常記錄日志
    /// 
    public class CustomerExceptionHandler : ILocalEventHandler, 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;
        }
    }
}

這樣寫,記錄的日志可以有很好的層次結構。

發布事件

定義了事件的格式和定義服務來訂閱事件后,我們來創建一個發布者。

我們修改一下?WebGlobalExceptionFilter

增加依賴注入:

        private readonly ILocalEventBus _localEventBus;

        public WebGlobalExceptionFilter(ILocalEventBus localEventBus)
        {
            _localEventBus = localEventBus;
        }

發布事件:

        public async Task OnExceptionAsync(ExceptionContext context)
        {

            if (!context.ExceptionHandled)
            {
                await _localEventBus.PublishAsync(new CustomerExceptionEvent(context.Exception,
                    context.ActionDescriptor?.DisplayName));
                    ...
                    ...

測試

創建一個 Action :

        [HttpGet("/T4")]
        public string MyWebApi4()
        {
            int a = 1;
            int b = 0;
            int c = a / b;
            return c.ToString();
        }

然后訪問?https://localhost:5001/T4?,會發現請求后報錯

在?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:

除了異常信息外,我們還可以很方便的知道異常發生在?TestController.MyWebApi4?這個位置。

記錄事件

如果在普通方法里面出現異常,我們這樣這樣記錄:

            catch (Exception ex)
            {
                ...
                new CustomerExceptionEvent(ex, MethodBase.GetCurrentMethod());
                ...
            }

MethodBase.GetCurrentMethod()?可以獲取當前正在運行的方法,獲得信息后將此參數傳遞給異常記錄服務,會自動解析出具體是哪個地方發生異常。

由于目前 Web 程序中還沒有編寫什么服務,因此我們先結合到異常日志功能中,后面編寫服務時,會再次用到事件總線。

完整代碼參考:https://github.com/whuanle/AbpBaseStruct/tree/master/src/4/AbpBase

源碼地址:https://github.com/whuanle/AbpBaseStruct

原文鏈接:https://www.cnblogs.com/whuanle/p/13679991.html

欄目分類
最近更新