Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

news2024/11/23 15:22:38

文章目录

    • 什么是 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.coloradmin.cn/o/1894318.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实验二 图像的代数运算

一、实验目的&#xff1a; 1&#xff0e;了解图像的算术运算在数字图像处理中的初步应用。 2&#xff0e;体会图像算术运算处理的过程和处理前后图像的变化。 二、实验内容&#xff1a; 1&#xff0e;图像的加法运算 图像相加一般用于对同一场景的多幅图像求平均效果&…

c语言回顾-内存操作函数

目录 前言 1.memcpy 函数 1.1函数介绍 1.2与strcpy的区别 1.3memcpy的模拟 2.memmove 函数 2.1函数介绍和使用 2.2函数的模拟 3.memset函数 3.1函数介绍 3.2函数的模拟 4.memcmp函数 4.1函数的使用 4.2函数的模拟 结束语 前言 在动态内存的章节中小编详细讲解了动…

pandas数据分析(7)

组合DataFrame 连接 如果只是要将多个DataFrame粘合在一起&#xff0c;那么concat函数是最佳选择。在默认情况下&#xff0c;concat会将DataFrame按行粘合在一起&#xff0c;同时会将各列自动对齐。 如果想要按列进行粘合&#xff0c;需要将axis设置为1&#xff1a; concat的特…

​香橙派AIpro测评:usb鱼眼摄像头的Camera图像获取

一、前言 近期收到了一块受到业界人士关注的开发板"香橙派AIpro",因为这块板子具有极高的性价比&#xff0c;同时还可以兼容ubuntu、安卓等多种操作系统&#xff0c;今天博主便要在一块832g的香橙派AI香橙派AIpro进行YoloV5s算法的部署并使用一个外接的鱼眼USB摄像头…

【Git 学习笔记】gitk 命令与 git log 其他参数的使用

1.7 用 gitk 查看提交历史 # make sure you have gitk installed $ which gitk /usr/bin/gitk # Sync the commit ID $ git checkout master && git reset --hard 13dcad # bring up the gitk interface, --all to see everything $ gitk --all &实测结果&#xf…

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法 0. 引言1. 关于m_track2. 关于m_range_ring3. 关于m_ellipse4. 关于m_windrose5. 结语 0. 引言 本篇介绍下m_map中绘制航迹图函数&#xff08;m_track&#xff09;、绘…

网络数据传输中的封装与解封装详解

注&#xff1a;机翻&#xff0c;未校对。 The goal of networks is to transmit data from one host to another. 网络的目标是将数据从一个主机传输到另一个主机。 Encapsulation 封装 To achieve this goal, each layer adds its own header to the data. A header contain…

2024年7月5日 十二生肖 今日运势

小运播报&#xff1a;2024年7月5日&#xff0c;星期五&#xff0c;农历五月三十 &#xff08;甲辰年庚午月庚午日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;狗、羊、虎 需要注意&#xff1a;鸡、牛、鼠 喜神方位&#xff1a;西北方 财神方位&#xff1a;正…

深圳合规新动向,这个关键环节要做好

随着全球商业环境的日益复杂化&#xff0c;企业合规管理已成为维护公司稳健运营和市场竞争力的核心要素。特别是对于位于创新前沿的深圳市&#xff0c;有效的合规管理系统不仅是满足法律和监管要求的必须&#xff0c;更是企业可持续发展的关键。 深圳市在全国率先探索并成功实…

#数据结构 笔记三

二叉树 1. 概念 二叉树Binary Tree是n个结点的有限集合。它或者是空集n0&#xff0c;或者是由一个根结点以及两颗互不相交、分别称为左子树和右子树的二叉树组成。 二叉树与普通有序树不同&#xff0c;二叉树严格区分左子和右子&#xff0c;即使只有一个子结点也要区分左右。…

cloudreve 设置开机服务

创建一个Systemd服务文件&#xff1a; 打开终端并创建一个新的服务文件&#xff1a; sudo nano /etc/systemd/system/cloudreve.service 在服务文件中添加以下内容&#xff1a; 根据你的设置调整路径和参数&#xff0c;然后将以下配置粘贴到文件中&#xff1a; [Unit] Descri…

二分查找及其变种

一、概念 二分查找算法&#xff08;Binary Search Algorithm&#xff09;是一种在有序数组中查找特定元素的高效搜索方法。 其基本思想是将目标值与数组中间的元素进行比较&#xff0c;如果目标值等于中间元素&#xff0c;则查找成功&#xff1b;如果目标值小于中间元素&…

指标和量化交易那些事儿

最近很多朋友都在给我说&#xff0c;我要盘中打板的指标&#xff0c;我要盘中自动交易。今天我们来梳理下关于指标和量化交易这些事儿&#xff01; 第一&#xff1a;什么是指标&#xff1f;股票指标是属于统计学的范畴&#xff0c;依据一定的数理统计方法&#xff0c;运用一些…

2024 年 亚太赛 APMCM (C题)中文赛道国际大学生数学建模挑战赛 | 量子计算的物流配送 | 数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题&#xff01; 完整内容可以在文章末尾领取&#xff01; 该段文字…

PyTorch - 神经网络基础

神经网络的主要原理包括一组基本元素&#xff0c;即人工神经元或感知器。它包括几个基本输入&#xff0c;例如 x1、x2… xn &#xff0c;如果总和大于激活电位&#xff0c;则会产生二进制输出。 样本神经元的示意图如下所述。 产生的输出可以被认为是具有激活电位或偏差的加权…

班迪录屏(Bandicam)7.0下载以及安装教程

最近有小伙伴私信我&#xff0c;问我有没有好用的录屏工具&#xff0c;今天给大家分享一个我一直在使用的录屏工具&#xff0c;也是解锁了V1P版本&#xff0c;绿色版打开就可以使用~ Bandicam录屏&#xff08;PC&#xff09; Bandicam录屏是一款专为捕捉屏幕精彩瞬间而设计的…

go——Swagger使用

一. 为什么后端需要配置Swagger 在前后端分离的项目中&#xff0c;后端配置swagger可以很好的帮助前端人员了解后端接口参数和数据传输。 Swagger是一个用于设计&#xff0c;构建和文档化API的开源框架。在Go语言中&#xff0c;Swagger可以帮助后端开发人员快速创建和定义RESTf…

电传动无杆飞机牵引车交付用户

自2019年起&#xff0c;我们计划做电传动控制&#xff0c;先后做了电传动水泥搅拌罐车罐体控制&#xff08;国内首创&#xff09;&#xff0c;初步理解了电机控制的特点。 20-21年接着做了10t飞机牵引车控制&#xff0c;还是电液控制联合的&#xff0c;把越野叉车的行驶控制方…

Python学习之小游戏--坦克大作战

今天跟视频学习了Python实现坦克大作战小游戏&#xff0c;挺有意思的&#xff0c;一起来玩吧~ 按空格发射子弹&#xff0c;上下左右键实现移动&#xff0c;ESC键无限复活。 import pygame,time,random from pygame.sprite import Sprite SCREEN_WIDTH800 SCREEN_HEIGHT500 BG…

玩机进阶教程----MTK芯片杂牌机 小品牌机型解除bl锁以及root的操作步骤解析

在玩机过程中会遇到很多小品牌机型或者杂牌机类的。大多都使用mtk芯片。而且基本很少有官方线刷包。在这些机型中玩机首先我们要想办法导出系统来制作线刷包。以免后续解锁bl或者root出现未知故障可以恢复原系统。 那么对于这些机型该如何进行备份固件和root呢。通过博文可以初…