zoukankan      html  css  js  c++  java
  • AOP +FreeSql 跨方法异步事务

    AOP +FreeSql 跨方法异步事务

    • Autofac.Extensions.DependencyInjection
    • Autofac.Extras.DynamicProxy
    • Castle.Core.AsyncInterceptor(异步方法AOP拦截)

    源码

    csproj

            <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
            <PackageReference Include="Autofac.Extras.DynamicProxy" Version="5.0.0" />
            <PackageReference Include="Castle.Core.AsyncInterceptor" Version="1.7.0" />
    

    使用Autofac实现特性标签,事务处理

    创建一个标识事务的特性标签

    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
    public class TransactionalAttribute : Attribute
    {
        /// <summary>
        /// 事务传播方式
        /// </summary>
        public Propagation Propagation { get; set; } = Propagation.Required;
    
        /// <summary>
        /// 事务隔离级别
        /// </summary>
        public IsolationLevel? IsolationLevel { get; set; }
    }
    

    Autofac

    Program.CS 替换默认的DI CreateHostBuilder方法

     Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())
    

    Startup.cs配置服务

    public void ConfigureContainer(ContainerBuilder builder)
    {
        builder.RegisterModule(new AutofacModule());
    }
    

    这里给BlogService方法注入UnitOfWorkInterceptor拦截处理。直接注入类。

    public class AutofacModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<UnitOfWorkInterceptor>();
            builder.RegisterType<UnitOfWorkAsyncInterceptor>();
            
            builder.RegisterType<BlogService>()
                .InterceptedBy(typeof(UnitOfWorkInterceptor))
                .EnableClassInterceptors();
                
    }
    
        List<Type> interceptorServiceTypes = new List<Type>()
        {
            typeof(UnitOfWorkInterceptor)
        };  
        //service所在dll
        Assembly servicesDllFile = Assembly.Load("LinCms.Application");
        
        builder.RegisterAssemblyTypes(servicesDllFile)
                .Where(a => a.Name.EndsWith("Service"))
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .PropertiesAutowired()// 属性注入
                .InterceptedBy(interceptorServiceTypes.ToArray())
                .EnableInterfaceInterceptors();
    

    AOP

        public class UnitOfWorkInterceptor : IInterceptor
        {
            private readonly UnitOfWorkAsyncInterceptor asyncInterceptor;
    
            public UnitOfWorkInterceptor(UnitOfWorkAsyncInterceptor interceptor)
            {
                asyncInterceptor = interceptor;
            }
    
            public void Intercept(IInvocation invocation)
            {
                asyncInterceptor.ToInterceptor().Intercept(invocation);
            }
        }
    
        public class UnitOfWorkAsyncInterceptor : IAsyncInterceptor
        {
            private readonly UnitOfWorkManager _unitOfWorkManager;
            private readonly ILogger<UnitOfWorkAsyncInterceptor> _logger;
            IUnitOfWork _unitOfWork;
    
            public UnitOfWorkAsyncInterceptor(UnitOfWorkManager unitOfWorkManager, ILogger<UnitOfWorkAsyncInterceptor> logger)
            {
                _unitOfWorkManager = unitOfWorkManager;
                _logger = logger;
            }
    
            private bool TryBegin(IInvocation invocation)
            {
                //_unitOfWork = _unitOfWorkManager.Begin(Propagation.Requierd);
                //return true;
                var method = invocation.MethodInvocationTarget ?? invocation.Method;
                var attribute = method.GetCustomAttributes(typeof(TransactionalAttribute), false).FirstOrDefault();
                if (attribute is TransactionalAttribute transaction)
                {
                    _unitOfWork = _unitOfWorkManager.Begin(transaction.Propagation, transaction.IsolationLevel);
                    return true;
                }
    
                return false;
            }
    
            /// <summary>
            /// 拦截同步执行的方法
            /// </summary>
            /// <param name="invocation"></param>
            public void InterceptSynchronous(IInvocation invocation)
            {
                if (TryBegin(invocation))
                {
                    int? hashCode = _unitOfWork.GetHashCode();
                    try
                    {
                        invocation.Proceed();
                        _logger.LogInformation($"----- 拦截同步执行的方法-事务 {hashCode} 提交前----- ");
                        _unitOfWork.Commit();
                        _logger.LogInformation($"----- 拦截同步执行的方法-事务 {hashCode} 提交成功----- ");
                    }
                    catch
                    {
                        _logger.LogError($"----- 拦截同步执行的方法-事务 {hashCode} 提交失败----- ");
                        _unitOfWork.Rollback();
                        throw;
                    }
                    finally
                    {
                        _unitOfWork.Dispose();
                    }
                }
                else
                {
                    invocation.Proceed();
                }
            }
    
            /// <summary>
            /// 拦截返回结果为Task的方法
            /// </summary>
            /// <param name="invocation"></param>
            public void InterceptAsynchronous(IInvocation invocation)
            {
                if (TryBegin(invocation))
                {
                    invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
                }
                else
                {
                    invocation.Proceed();
                }
            }
    
            private async Task InternalInterceptAsynchronous(IInvocation invocation)
            {
                string methodName =
                    $"{invocation.MethodInvocationTarget.DeclaringType?.FullName}.{invocation.Method.Name}()";
                int? hashCode = _unitOfWork.GetHashCode();
    
                using (_logger.BeginScope("_unitOfWork:{hashCode}", hashCode))
                {
                    _logger.LogInformation($"----- async Task 开始事务{hashCode} {methodName}----- ");
    
                    invocation.Proceed();
    
                    try
                    {
                     //处理Task返回一个null值的情况会导致空指针
                        if (invocation.ReturnValue != null)
                        {
                            await (Task)invocation.ReturnValue;
                        }
                        _unitOfWork.Commit();
                        _logger.LogInformation($"----- async Task 事务 {hashCode} Commit----- ");
                    }
                    catch (System.Exception)
                    {
                        _unitOfWork.Rollback();
                        _logger.LogError($"----- async Task 事务 {hashCode} Rollback----- ");
                        throw;
                    }
                    finally
                    {
                        _unitOfWork.Dispose();
                    }
                }
    
            }
    
    
            /// <summary>
            /// 拦截返回结果为Task<TResult>的方法
            /// </summary>
            /// <param name="invocation"></param>
            /// <typeparam name="TResult"></typeparam>
            public void InterceptAsynchronous<TResult>(IInvocation invocation)
            {
                invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
            }
    
            private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
            {
                TResult result;
                if (TryBegin(invocation))
                {
                    string methodName = $"{invocation.MethodInvocationTarget.DeclaringType?.FullName}.{invocation.Method.Name}()";
                    int hashCode = _unitOfWork.GetHashCode();
                    _logger.LogInformation($"----- async Task<TResult> 开始事务{hashCode} {methodName}----- ");
    
                    try
                    {
                        invocation.Proceed();
                        result = await (Task<TResult>)invocation.ReturnValue;
                        _unitOfWork.Commit();
                        _logger.LogInformation($"----- async Task<TResult> Commit事务{hashCode}----- ");
                    }
                    catch (System.Exception)
                    {
                        _unitOfWork.Rollback();
                        _logger.LogError($"----- async Task<TResult> Rollback事务{hashCode}----- ");
                        throw;
                    }
                    finally
                    {
                        _unitOfWork.Dispose();
                    }
                }
                else
                {
                    invocation.Proceed();
                    result = await (Task<TResult>)invocation.ReturnValue;
                }
                return result;
            }
        }
    

    没有接口,必须使用virtual虚方法。

        public class BlogService
        {
            /// <summary>
            /// 当出现异常时,不会插入数据
            /// </summary>
            /// <param name="createBlogDto"></param>
            [Transactional]
            public virtual void CreateBlogTransactional(CreateBlogDto createBlogDto)
            {
                Blog blog = _mapper.Map<Blog>(createBlogDto);
                blog.CreateTime = DateTime.Now;
                _blogRepository.Insert(blog);
    
                List<Tag> tags = new List<Tag>();
                createBlogDto.Tags.ForEach(r =>
                {
                    tags.Add(new Tag { TagName = r });
                });
                if (createBlogDto.Title == "abc")
                {
                    throw new Exception("test exception");
                }
                _tagRepository.Insert(tags);
            }
        }
    

    作者: 、天上有木月OvO

    出处:https://cnblogs.com/igeekfan

    联系:luoyunchong@foxmail.com

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。
  • 相关阅读:
    deploy.sh
    error cross-zip@4.0.0: The engine "node" is incompatible with this module. Expected version ">=12.10". Got "10.16.3"
    could not compile E:\sqjw-web\node_modules\@nuxt\vue-app\template\App.js:connot resolve "element-ui/lib/theme-chalk/index.css"
    js如何查看元素类型
    修改tomcat浏览器地址栏图标
    a different object with the same identifier value was already associated withthe session异常解决方案
    org.springframework.dao.InvalidDataAccessApiUsageException:The given object has a null identifi的解决方案
    利用JS验证查询参数-选择月份后必须选择年份
    算术运算函数
    数据的输出与数值传递
  • 原文地址:https://www.cnblogs.com/igeekfan/p/aop-freesql-autofac.html
Copyright © 2011-2022 走看看