Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 實踐 AOP 以及實現事務、用戶填充功能

文章目錄

    • 什么是 AOP ?
    • .Net Core 中 有哪些 AOP 框架?
    • 基于 Castle DynamicProxy 實現 AOP
    • IOC中使用 Castle DynamicProxy
    • 實現事務管理
    • 實現用戶自動填充

什么是 AOP ?

AOP(Aspect-Oriented Programming,面向切面編程)是一種編程范式,旨在通過將橫切關注點(cross-cutting concerns)從主要業務邏輯中分離出來,以提高代碼的模塊化性、可維護性和復用性。

在傳統的面向對象編程中,我們通常通過類和對象來組織和實現功能。然而,某些功能,如日志記錄、事務管理、安全性檢查等,可能會跨越多個對象和模塊,這種跨越稱為橫切關注點。AOP 的核心思想是將這些橫切關注點從業務邏輯中分離出來,通過特定的機制將它們應用到代碼中,而不是通過直接修改業務邏輯來實現。

.Net Core 中 有哪些 AOP 框架?

PostSharp(收費)

PostSharp是一個功能強大的AOP框架,它通過編譯器插件的形式集成到Visual Studio中。PostSharp支持編譯時AOP(通過C#特性應用切面),并提供了豐富的切面類型,包括方法攔截、屬性訪問攔截、異常處理等。它還提供了商業支持和豐富的文檔。

Castle DynamicProxy

Castle DynamicProxy是Castle項目的一部分,它允許開發者在運行時動態創建代理類,這些代理類可以攔截對目標對象的調用,并在調用前后執行自定義邏輯。雖然它本身不是一個完整的AOP框架,但它經常被用作構建AOP解決方案的基礎。

AspectCore Framework

AspectCore 是一個開源的 AOP 框架,專為 .NET Core 設計。它提供了基于動態代理的運行時切面和方法攔截機制,支持常見的切面編程需求,如日志、緩存、事務等。

基于 Castle DynamicProxy 實現 AOP

1. 安裝Castle.Core NuGet包

Install-Package Castle.Core

2. 定義接口和類

假設你有一個接口和一個實現了該接口的類,你想要攔截這個類的方法調用。

public interface IMyService  
{  void PerformAction();  
}   
public class MyService : IMyService  
{  public void PerformAction()  {  Console.WriteLine("Action performed.");  }  
}

3. 創建攔截器

接下來,你需要創建一個攔截器類,該類將實現IInterceptor接口。在這個接口的實現中,你可以定義在調用目標方法之前和之后要執行的邏輯。

using Castle.DynamicProxy;  public class MyInterceptor : IInterceptor  
{  public void Intercept(IInvocation invocation)  {  // 在調用目標方法之前執行的邏輯  Console.WriteLine("Before method: " + invocation.Method.Name);  // 調用目標方法  invocation.Proceed();  // 在調用目標方法之后執行的邏輯  Console.WriteLine("After method: " + invocation.Method.Name);  }  
}

4. 創建代理并調用方法

最后,你需要使用ProxyGenerator類來創建MyService的代理實例,并指定攔截器。然后,你可以像使用普通對象一樣調用代理的方法,但攔截器中的邏輯會在調用發生時執行。

using Castle.DynamicProxy;  public class Program  
{  public static void Main(string[] args)  {  var generator = new ProxyGenerator();  var interceptor = new MyInterceptor();  // 創建MyService的代理實例,并指定攔截器  var proxy = generator.CreateInterfaceProxyWithTarget<IMyService>(  new MyService(), interceptor);  // 調用代理的方法,攔截器中的邏輯將被執行  proxy.PerformAction();  }  
}

注意,上面的示例使用了接口代理(CreateInterfaceProxyWithTarget),這意味著你的目標類必須實現一個或多個接口。如果你想要代理一個類而不是接口,你可以使用CreateClassProxyWithTarget方法(但這通常用于需要代理非虛方法或字段的場景,且要求目標類是可繼承的)。

IOC中使用 Castle DynamicProxy

由于IOC容器(如Microsoft的IServiceCollectionIServiceProvider)通常不直接支持AOP,所以用 Autofac
1. 安裝必要的 NuGet 包

首先,確保你的項目中安裝了以下 NuGet 包:

Install-Package Autofac
Install-Package Autofac.Extensions.DependencyInjection
Install-Package Autofac.Extras.DynamicProxy
Install-Package Castle.Core

2. 創建服務接口和實現類

    public class User{public long Id { get; set; }public string Name { get; set; }public long CreateUserId { get; set; }public string CreateUserName { get; set; }public DateTime CreateTime { get; set; }public long UpdateUserId { get; set; }public string UpdateUserName { get; set; }public DateTime UpdateTime { get; set; }}    public interface IUserService{void Test();Task<int> TaskTest();void Add(User user);void Update(User user);}public class UserService : IUserService{public void Test(){Console.WriteLine("Test");}public async Task<int> TaskTest(){await Console.Out.WriteLineAsync("TaskTest");return 1;}public void Add(User user){Console.WriteLine(user.CreateUserId);Console.WriteLine(user.CreateUserName);}public void Update(User user){Console.WriteLine(user.UpdateUserId);Console.WriteLine(user.UpdateUserName);}}[ApiController][Route("[controller]")]public class UserController : ControllerBase{readonly IUserService _userService;public UserController(IUserService userService){_userService = userService;}        [HttpGet][Route("/taskTest")]public async Task<string> TaskTest(){await _userService.TaskTest();return "ok";}[HttpGet][Route("/test")]public string Test(){_userService.Test();return "ok";}[HttpGet][Route("/add")]public string Add(){_userService.Add(new Model.User { Name = "張三" });return "ok";}[HttpGet][Route("/update")]public string Update(){_userService.Update(new Model.User { Name = "張三" });return "ok";}}

3. 創建攔截器類

創建一個實現 IInterceptor 接口的攔截器類 LoggingInterceptor,用于攔截方法調用并添加日志記錄:

public class LoggingInterceptor : IInterceptor
{public void Intercept(IInvocation invocation){Console.WriteLine($"Before executing: {invocation.Method.Name}");invocation.Proceed(); // 調用原始方法Console.WriteLine($"After executing: {invocation.Method.Name}");}
}

4. 配置 Autofac 容器

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //使用Autofac.ConfigureContainer<ContainerBuilder>(autofacBuilder =>{autofacBuilder.RegisterType<LoggingInterceptor>();autofacBuilder.RegisterType<UserService>().As<IUserService>    ().SingleInstance().AsImplementedInterfaces()    .EnableInterfaceInterceptors() // 啟用接口攔截器.InterceptedBy(typeof(LoggingInterceptor)); //指定攔截器});

與Autofac集成時,配置攔截器主要有兩種方式

使用 InterceptAttribute 特性

這種方式通過在接口或類上添加[Intercept(typeof(YourInterceptor))]特性來指定攔截器。然后,在Autofac注冊時,啟用接口或類的攔截器。(通常不推薦在類上直接添加,因為這會使類與Autofac緊密耦合)

 [Intercept(typeof(UserAutoFillInterceptor))]public class UserService : IUserService
{  public void Test(){Console.WriteLine("Test");} 
}autofacBuilder.RegisterType<UserService>().As<IUserService>().EnableInterfaceInterceptors() // 啟用接口攔截器

使用 InterceptedBy() 方法

這種方式不依賴于[Intercept]特性,而是在注冊服務時直接使用InterceptedBy()方法來指定攔截器。

                            autofacBuilder.RegisterType<UserService>().As<IUserService>()    .EnableInterfaceInterceptors() // 啟用接口攔截器.InterceptedBy(typeof(LoggingInterceptor)); //指定攔截器

實現事務管理

攔截器基類

    /// <summary>/// 攔截基類/// </summary>/// <typeparam name="T"></typeparam>public abstract class BaseInterceptor<T> : IInterceptor{protected readonly ILogger<T> _logger;public BaseInterceptor(ILogger<T> logger){_logger = logger;}/// <summary>/// 攔截方法/// </summary>/// <param name="invocation"></param>public virtual void Intercept(IInvocation invocation){try{Method = invocation.MethodInvocationTarget ?? invocation.Method;InterceptHandle(invocation);}catch (Exception ex){_logger.LogError(ex, ex.Message);throw ex;}         }/// <summary>/// 攔截處理/// </summary>/// <param name="invocation"></param>public abstract void InterceptHandle(IInvocation invocation);protected MethodInfo Method{ get; set; }public static bool IsAsyncMethod(MethodInfo method){return (method.ReturnType == typeof(Task) ||(method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)));}
}

事務特性:用來判斷是否需要事務管理的

    /// <summary>/// 事務特性/// </summary>[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]public class TransactionalAttribute : Attribute{public TransactionalAttribute(){Timeout = 60;}/// <summary>/// /// </summary>public int Timeout { get; set; }/// <summary>/// 事務隔離級別/// </summary>public IsolationLevel IsolationLevel { get; set; }/// <summary>/// 事務傳播方式/// </summary>public Propagation Propagation { get; set; }}/// <summary>/// 事務傳播方式/// </summary>public enum Propagation{/// <summary>/// 默認:如果當前沒有事務,就新建一個事務,如果已存在一個事務中,加入到這個事務中。/// </summary>Required = 0,/// <summary>/// 使用當前事務,如果沒有當前事務,就拋出異常/// </summary>Mandatory = 1,/// <summary>/// 以嵌套事務方式執行/// </summary>Nested = 2,}

事務攔截器:處理事務的

    /// <summary>/// 事務攔截器/// </summary>public class TransactionalInterceptor : BaseInterceptor<TransactionalInterceptor>{public TransactionalInterceptor(ILogger<TransactionalInterceptor> logger) : base(logger){}public override void InterceptHandle(IInvocation invocation){if (Method.GetCustomAttribute<TransactionalAttribute>(true) == null && Method.DeclaringType?.GetCustomAttribute<TransactionalAttribute>(true) == null){invocation.Proceed();}else{try{Console.WriteLine("開啟事務");//執行方法invocation.Proceed();// 異步獲取異常,先執行if (IsAsyncMethod(invocation.Method)){var result = invocation.ReturnValue;if (result is Task){Task.WaitAll(result as Task);}}Console.WriteLine("提交事務");}catch (Exception ex){Console.WriteLine("回滾事務");_logger.LogError(ex, ex.Message);throw ex;}}}}

接口上加入事務特性

    //[Transactional]public class UserService : IUserService{[Transactional]public void Test(){Console.WriteLine("Test");}}

注入IOC

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()).ConfigureContainer<ContainerBuilder>(autofacBuilder =>{autofacBuilder.RegisterType<TransactionalInterceptor>();                                      autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces().EnableInterfaceInterceptors().InterceptedBy(typeof(TransactionalInterceptor));});

測試

image

實現用戶自動填充

上下戶用戶

    public interface IHttpContextUser{long UserId { get; }string UserName { get;}}public class HttpContextUser : IHttpContextUser{private readonly IHttpContextAccessor _accessor;public HttpContextUser(IHttpContextAccessor accessor){_accessor = accessor;}public long UserId{get{return 1; //這里暫時是寫死的if (int.TryParse(_accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Sid), out var userId)){return userId;}return default;}}public string UserName{get{return "admin"; //這里暫時是寫死的return _accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name) ?? "";}}}

注入IOC

           builder.Services.AddHttpContextAccessor();builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()).ConfigureContainer<ContainerBuilder>(autofacBuilder =>{autofacBuilder.RegisterType<HttpContextUser>().As<IHttpContextUser>().SingleInstance().AsImplementedInterfaces();autofacBuilder.RegisterType<UserAutoFillInterceptor>();autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces().EnableInterfaceInterceptors().InterceptedBy(typeof(UserAutoFillInterceptor));});

用戶自動攔截器:處理用戶填充的

    /// <summary>/// 用戶自動填充攔截器/// </summary>public class UserAutoFillInterceptor : BaseInterceptor<UserAutoFillInterceptor>{private readonly IHttpContextUser _user;public UserAutoFillInterceptor(ILogger<UserAutoFillInterceptor> logger,IHttpContextUser user) : base(logger){_user = user;}public override void InterceptHandle(IInvocation invocation){//對當前方法的特性驗證if (Method.Name?.ToLower() == "add" || Method.Name?.ToLower() == "update"){if (invocation.Arguments.Length == 1 && invocation.Arguments[0].GetType().IsClass){dynamic argModel = invocation.Arguments[0];var getType = argModel.GetType();if (Method.Name?.ToLower() == "add"){if (getType.GetProperty("CreateUserId") != null){argModel.CreateUserId = _user.UserId;}if (getType.GetProperty("CreateUserName") != null){argModel.CreateUserName = _user.UserName;}if (getType.GetProperty("CreateTime") != null){argModel.CreateTime = DateTime.Now;}}if (getType.GetProperty("UpdateUserId") != null){argModel.UpdateUserId = _user.UserId;}if (getType.GetProperty("UpdateUserName") != null){argModel.UpdateUserName = _user.UserName;}if (getType.GetProperty("UpdateTime") != null){argModel.UpdateTime = DateTime.Now;}}invocation.Proceed();}else{invocation.Proceed();}}}

測試

image

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/40724.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/40724.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/40724.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenCV——把YOLO格式的圖片目標截圖,并按目標類別保存

import os import cv2def get_class_folder(catagetory,class_id, base_folder):# 根據類別ID創建文件夾路徑class_folder os.path.join(base_folder, catagetory[int(class_id)])if not os.path.exists(class_folder):os.makedirs(class_folder)return class_folderdef crop_…

VPN是什么?

VPN&#xff0c;全稱Virtual Private Network&#xff0c;即“虛擬私人網絡”&#xff0c;是一種在公共網絡&#xff08;如互聯網&#xff09;上建立加密、安全的連接通道的技術。簡單來說&#xff0c;VPN就像是一條在公共道路上鋪設的“秘密隧道”&#xff0c;通過這條隧道傳輸…

圖像的反轉

圖像顏色的反轉一般分為兩種&#xff1a;一種是灰度圖片的顏色反轉&#xff0c;另一種是彩色圖像的顏色反轉。 本節使用的原圖如下&#xff1a; 1.1 灰度圖像顏色反轉 灰度圖像每個像素點只有一個像素值來表示&#xff0c;色彩范圍在0-255之間&#xff0c;反轉方法255-當前像…

信創產業政策,信創測試方面

信創產業的政策支持主要體現在多個方面&#xff0c;這些政策旨在推動產業的快速發展&#xff0c;加強自主創新能力&#xff0c;保障國家信息安全&#xff0c;以及促進產業結構的優化升級。 首先&#xff0c;政府通過財政支持、稅收優惠等方式&#xff0c;加大對信創產業的資金…

8.ApplicationContext常見實現

ClassPathXmlApplicationContext 基于classpath下xml格式的配置文件來創建 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-i…

Flutter——最詳細(Drawer)使用教程

背景 應用左側或右側導航面板&#xff1b; 屬性作用elevation相當于陰影的大小 import package:flutter/material.dart;class CustomDrawer extends StatelessWidget {const CustomDrawer({Key? key}) : super(key: key);overrideWidget build(BuildContext context) {return…

解決SeaTunnel 2.3.4版本寫入S3文件報錯問題

在使用Apache SeaTunnel時&#xff0c;我遇到了一個寫入S3文件的報錯問題。通過深入調試和分析&#xff0c;找到了問題所在&#xff0c;并提出了相應的解決方案。 本文將詳細介紹報錯情況、參考資料、解決思路以及后續研究方向&#xff0c;希望對大家有幫助&#xff01; 一、…

代碼隨想錄算法訓練營第二天|【數組】977.有序數組的平方

題目 給你一個按 非遞減順序 排序的整數數組 nums&#xff0c;返回 每個數字的平方 組成的新數組&#xff0c;要求也按 非遞減順序 排序。 示例 1&#xff1a; 輸入&#xff1a;nums [-4,-1,0,3,10] 輸出&#xff1a;[0,1,9,16,100] 解釋&#xff1a;平方后&#xff0c;數組…

修改頭文件版本需要修改的文件

以修改ui的頭文件版本為例&#xff0c;還需要同時更新 PJ10PC20240120041_c928\components\master-t5\hikauto\module\app\include PJ10PC20240120041_c928\components\master-t5\hikauto\module\app\include\dsp PJ10PC20240120041_c928\components\master-t5\hikauto\incl…

C# Halcon目標檢測算法

在Halcon中進行目標檢測可以使用傳統的計算機視覺方法&#xff0c;也可以使用深度學習的方法。Halcon提供了豐富的函數庫來處理這些任務&#xff0c;而在C#中使用Halcon&#xff0c;你需要通過Halcon .NET接口。 以下是使用Halcon進行目標檢測的一般步驟&#xff0c;這里我將給…

DDL:針對于數據庫、數據表、數據字段的操作

數據庫的操作 # 查詢所有數據 SHOW DATABASE; #創建數據庫 CREATE DATABASE 2404javaee; #刪除數據庫 DROP DATABASE 2404javaee; 數據表的操作 #創建表 CREATE TABLE s_student( name VARCHAR(64), s_sex VARCHAR(32), age INT(3), salary FLOAT(8,2), c_course VARC…

Windows 的 MFC開發的使用示例——講得挺好的

【Visual Studio 2019】創建 MFC 桌面程序 ( 安裝 MFC 開發組件 | 創建 MFC 應用 | MFC 應用窗口編輯 | 為按鈕添加點擊事件 | 修改按鈕文字 | 打開應用 )-騰訊云開發者社區-騰訊云 (tencent.com)

【算法】(C語言):堆排序

堆&#xff08;二叉樹的應用&#xff09;&#xff1a; 完全二叉樹。最大堆&#xff1a;每個節點比子樹所有節點的數值都大&#xff0c;根節點是最大值。父子索引號關系&#xff08;根節點為0&#xff09;&#xff1a;&#xff08;向上&#xff09;子節點x&#xff0c;父節點(x…

datawhale大模型應用開發夏令營學習筆記一

參考自 基于LangChainLLM的本地知識庫問答&#xff1a;從企業單文檔問答到批量文檔問答datawhale的llm-universe 作者現在在datawhale夏令營的大模型應用開發這個班中&#xff0c;作為一個小白&#xff0c;為了能為團隊做出一點貢獻&#xff0c;現在就要開始學習怎么使用langch…

實戰教程:如何用JavaScript構建一個功能強大的音樂播放器,兼容本地與在線資源

項目地址&#xff1a;Music Player App 作者&#xff1a;Reza Mehdikhanlou 視頻地址&#xff1a;youtube 我將向您展示如何使用 javascript 編寫音樂播放器。我們創建一個項目&#xff0c;您可以使用 javascript 從本地文件夾或任何 url 播放音頻文件。 項目目錄 assets 1…

頂級10大AI測試工具

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

暑期編程預習指南

暑期編程預習指南 高考結束后&#xff0c;迎來的是一段難得的假期時光。對于那些有志于踏入IT領域的高考生來說&#xff0c;這段時間無疑是一個重要的起點。為了幫助你們更好地利用這個假期&#xff0c;為未來的學習和職業生涯打下堅實的基礎&#xff0c;特此提供一份編程預習…

JWT入門

JWT與TOKEN JWT&#xff08;JSON Web Token&#xff09;是一種基于 JSON 格式的輕量級安全令牌&#xff0c;通常用于在網絡應用間安全地傳遞信息。而“token”一詞則是一個更廣泛的術語&#xff0c;用來指代任何形式的令牌&#xff0c;用于在計算機系統中進行身份驗證或授權。J…

【?講解下Laravel為什么會成為最優雅的PHP框架?】

&#x1f3a5;博主&#xff1a;程序員不想YY啊 &#x1f4ab;CSDN優質創作者&#xff0c;CSDN實力新星&#xff0c;CSDN博客專家 &#x1f917;點贊&#x1f388;收藏?再看&#x1f4ab;養成習慣 ?希望本文對您有所裨益&#xff0c;如有不足之處&#xff0c;歡迎在評論區提出…

cloudreve 設置開機服務

創建一個Systemd服務文件&#xff1a; 打開終端并創建一個新的服務文件&#xff1a; sudo nano /etc/systemd/system/cloudreve.service 在服務文件中添加以下內容&#xff1a; 根據你的設置調整路徑和參數&#xff0c;然后將以下配置粘貼到文件中&#xff1a; [Unit] Descri…