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

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

时间:2024-07-04 22:09:35浏览次数:1  
标签:Core 拦截器 DynamicProxy void get Asp user invocation public

目录

什么是 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

标签:Core,拦截器,DynamicProxy,void,get,Asp,user,invocation,public
From: https://www.cnblogs.com/vic-tory/p/18284779

相关文章

  • C#面:ASP.NET Core ⽐ ASP.NET 更具优势的地⽅是什么?
    ASP.NETCore相对于ASP.NET具有以下几个优势:跨平台支持:ASP.NETCore是跨平台的,可以在Windows、Linux和macOS等多个操作系统上运行。这使得开发人员可以选择更适合他们的操作系统来进行开发和部署。更轻量级:ASP.NETCore是一个轻量级的框架,它具有更小的内存占用和更快的启动......
  • EFCore 在APS.NET MVC中使用
    1.创建一个APS.NETMVC项目 2.安装Nuget包Microsoft.EntityFrameworkCore.DesignMicrosoft.EntityFrameworkCore.SqlServer3.在Models中添加Book实体类publicclassBook{///<summary>///id///</summary>publicintId{get;set;}///......
  • 解决 .NET Core 和 nginx 双重配置 CORS 问题
    解决.NETCore和nginx双重配置CORS问题在开发基于.NETCore的Web应用时,经常会遇到跨域资源共享(CORS)的问题。跨域请求是指浏览器从一个不同的域、协议或端口访问资源。在现代Web开发中,跨域请求非常常见,但为了安全,浏览器会阻止这些请求,除非服务器明确允许。最近在配置......
  • 使用 EFCore简单入门(实体类生成数据库表)
    1.安装Nuget包Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Tools2.创建Book,Post两个实体类publicclassBook{///<summary>///id///</summary>publicintId{get;set;}///<summary>///......
  • Pgsql帮助类 netcore
    十年河东,十年河西,莫欺少年穷学无止境。精益求精usingNpgsql;usingSystem;usingNpgsqlTypes;usingSystem.Data;usingSystem.Collections.Generic;usingSystem.Reflection;usingSystem.Configuration;usingswapCommon;namespacePgSqlHelper{publiccla......
  • asp.net core 通过SFTP下载文件
     引用第三方库:Ssh.Net try{using(varclient=newSftpClient(ftpHost,ftpPort,ftpUsername,ftpPassword)){client.Connect();IEnumerable<ISftpFile>fileEnumerable=client.ListDirectory(remoteDirectory,null).Where(g......
  • linux安装netcore nginx postgresql ssh
    sudosu1.安装软件$sudoaptinstall软件名2.卸载软件$sudoaptremove软件名3.更新可用软件包列表$sudoaptupdate4.更新已安装的包$sudoaptupgrade通常安装完ubuntu之后,可以先使用upgrade更新一下当前系统中可以升级的的软件包$sudoaptupdate$sudoap......
  • Acore_characters数据库全面解释
    acore_characters数据库目前有95个表,主要存储与用户账户有关的信息。account_data包含有关客户账户和设置的数据。accountId玩家账户ID。typeValueDescription0全局-账户配置缓存2全局-账户绑定缓存4全局-账户巨集缓存time最后修改的时间。data未......
  • Acore_auth数据库全面解释
    简单说吧,如果你不会源码修改编译,单单玩数据库和DBC就能做出很有特色的端。Azerothcore的数据库是Mysql,所以你得下载个SQL管理工具,大家比较常用的是NavicatSQL,我常用的是HeidiSQL(免费)。连接数据库的话,地址、用户名和密码请自行查找服务端内的worldserver.conf文件,示例:LoginDataba......
  • Acore-world数据库之各类loot-template表全面解释
    前言Acore-world数据库涉及到掉落(不含扒窃)的表一共有11个,分别是:序号表名说明1creature_loot_template生物掉落表,其entry值对应creature_template表的和lootid值2disenchant_loot_template附魔分解掉落表,其entry值对应item_template表的DisenchantID值3fish......