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);            
    }
    
  • 相关阅读:
    Leetcode 1489找到最小生成树李关键边和伪关键边
    Leetcode 113 路径总和 II
    hdu 1223 还是畅通工程
    hdu 1087 Super Jumping! Jumping! Jumping!
    hdu 1008 Elevator
    hdu 1037 Keep on Truckin'
    湖工oj 1241 畅通工程
    湖工oj 1162 大武汉局域网
    hdu 2057 A + B Again
    poj 2236 Wireless Network
  • 原文地址:https://www.cnblogs.com/jianxuanbing/p/7199457.html
Copyright © 2011-2022 走看看