zoukankan      html  css  js  c++  java
  • [AOP系列]Autofac+Castle实现AOP事务

    一、前言

    最近公司新项目,需要搭架构进行开发,其中需要保证事务的一致性,经过一番查找,发现很多博文都是通过Spring.Net、Unity、PostSharp、Castle Windsor这些方式实现AOP的。但是这不是我想要的,因此一番查找后,使用Autofac、DynamicProxy该方式实现AOP。

    二、使用AOP的优势

    博主觉得它的优势主要表现在:

    • 将通用功能从业务逻辑中抽离出来,就可以省略大量重复代码,有利于代码的操作和维护。
    • 在软件设计时,抽出通用功能(切面),有利于软件设计的模块化,降低软件架构的复杂程度。也就是说通用的功能就是一个单独的模块,在项目的主业务里面是看不到这些通用功能的设计代码的。

    三、引用库

    • Autofac:4.6
    • Autofac.Extras.DynamicProxy:4.1.0
    • Castle.Core:3.2.2

    四、实现思路

    4.1 定义属性

    定义属性,通过当前方法是否包含该属性进行判断开启事务,如果存在该属性则开启事务,否则忽略事务。
    事务属性可以设置超时时间、事务范围以及事务隔离级别。
    代码如下:

    /// <summary>
    /// 开启事务属性
    /// </summary>
    [AttributeUsage(AttributeTargets.Method,Inherited = true)]
    public class TransactionCallHandlerAttribute:Attribute
    {
        /// <summary>
        /// 超时时间
        /// </summary>
        public int Timeout { get; set; }
    
        /// <summary>
        /// 事务范围
        /// </summary>
        public TransactionScopeOption ScopeOption { get; set; }
    
        /// <summary>
        /// 事务隔离级别
        /// </summary>
        public IsolationLevel IsolationLevel { get; set; }
    
        public TransactionCallHandlerAttribute()
        {
            Timeout = 60;
            ScopeOption=TransactionScopeOption.Required;
            IsolationLevel=IsolationLevel.ReadCommitted;
        }
    }
    

    4.2 切面实现

    获取当前方法是否包含TransactionCallHandlerAttribute该属性,如果有该属性则开启事务。
    本人在此处加入开发模式判断,用于没设置MSDTC产生异常的问题,如果不需要可忽略。
    另外日志功能自行实现即可。
    代码如下:

    /// <summary>
    /// 事务 拦截器
    /// </summary>
    public class TransactionInterceptor:IInterceptor
    {
        //可自行实现日志器,此处可忽略
        /// <summary>
        /// 日志记录器
        /// </summary>
        private static readonly ILog Logger = Log.GetLog(typeof(TransactionInterceptor));
    
        // 是否开发模式
        private bool isDev = false;
        public void Intercept(IInvocation invocation)
        {
            if (!isDev)
            {
                MethodInfo methodInfo = invocation.MethodInvocationTarget;
                if (methodInfo == null)
                {
                    methodInfo = invocation.Method;
                }
                                
                TransactionCallHandlerAttribute transaction =
                    methodInfo.GetCustomAttributes<TransactionCallHandlerAttribute>(true).FirstOrDefault();
                if (transaction != null)
                {
                    TransactionOptions transactionOptions = new TransactionOptions();
                    //设置事务隔离级别
                    transactionOptions.IsolationLevel = transaction.IsolationLevel;
                    //设置事务超时时间为60秒
                    transactionOptions.Timeout = new TimeSpan(0, 0, transaction.Timeout);
                    using (TransactionScope scope = new TransactionScope(transaction.ScopeOption, transactionOptions))
                    {
                        try
                        {
                            //实现事务性工作
                            invocation.Proceed();
                            scope.Complete();
                        }
                        catch (Exception ex)
                        {
                            // 记录异常
                            throw ex;
                        }
                    }
                }
                else
                {
                    // 没有事务时直接执行方法
                    invocation.Proceed();
                }
            }
            else
            {
                // 开发模式直接跳过拦截
                invocation.Proceed();
            }
        }
    }
    

    4.3 切面注入

    博主对Autofac进行了封装,可能与你们的配置不一样,但是,Load(ContainerBuilder builder)该方法内容是一致的,因此注入方式一致的。
    通过定义IDependency空接口方式,需要注入的类则继承该接口即可。
    代码如下:

    /// <summary>
    /// 应用程序IOC配置
    /// </summary>
    public class IocConfig : ConfigBase
    {
        // 重写加载配置
        protected override void Load(ContainerBuilder builder)
        {
            var assembly = this.GetType().GetTypeInfo().Assembly;
            builder.RegisterType<TransactionInterceptor>();
            builder.RegisterAssemblyTypes(assembly)
                .Where(type => typeof(IDependency).IsAssignableFrom(type) && !type.GetTypeInfo().IsAbstract)
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .EnableInterfaceInterceptors()
                .InterceptedBy(typeof(TransactionInterceptor));
        }
    }
    

    五、例子

    /// <summary>
    /// 添加文章
    /// </summary>
    /// <param name="name"></param>
    [TransactionCallHandler]
    public void AddArticle(string name)
    {
        BasArticle model=new BasArticle();
        model.ArticleID = Guid.Empty;//故意重复,判断是否会回滚。
        model.Code = TimestampId.GetInstance().GetId();
        model.Name = name;
        model.Status = 1;
        model.Creater = "测试";
        model.Editor = "测试";
        this._basArticleRepository.Insert(model);            
    }
    
  • 相关阅读:
    HttpClientUtil的工具类请求三方API
    Linux下使用docker搭建ftp服务器
    Springboot2.0.4整合Mybatisplus
    Springboot前后端分离项目,配置跨域
    Nokia S40 系统配置
    "Your profile could not be opened correctly" Google Chromium Browser 错误纠正
    CLR AppDomain
    iTunes下载提速
    Which Programming Language?
    linux下制作软盘镜像文件
  • 原文地址:https://www.cnblogs.com/jianxuanbing/p/7199457.html
Copyright © 2011-2022 走看看