[AOPシリーズ]Autofac+CastleによるAOPトランザクションの実装

4980 ワード

一、前言
最近、会社の新しいプロジェクトは、アーキテクチャを構築して開発する必要があります.その中で、トランザクションの一貫性を保証する必要があります.検索したところ、多くのブログが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属性の定義
    属性を定義し、現在のメソッドにその属性が含まれているかどうかを判断してトランザクションをオープンし、その属性が存在する場合はトランザクションをオープンし、そうでない場合はトランザクションを無視します.トランザクション・プロパティでは、タイムアウト時間、トランザクション範囲、およびトランザクション独立性レベルを設定できます.コードは次のとおりです.
    /// 
    ///       
    /// 
    [AttributeUsage(AttributeTargets.Method,Inherited = true)]
    public class TransactionCallHandlerAttribute:Attribute
    {
        /// 
        ///     
        /// 
        public int Timeout { get; set; }
    
        /// 
        ///     
        /// 
        public TransactionScopeOption ScopeOption { get; set; }
    
        /// 
        ///       
        /// 
        public IsolationLevel IsolationLevel { get; set; }
    
        public TransactionCallHandlerAttribute()
        {
            Timeout = 60;
            ScopeOption=TransactionScopeOption.Required;
            IsolationLevel=IsolationLevel.ReadCommitted;
        }
    }

    4.2断面実装
    現在のメソッドにTransactionCallHandlerAttributeの属性が含まれているかどうかを取得し、その属性があればトランザクションを開始します.私はここで開発モード判定を加えて、MSDTCを設定していないで異常な問題を発生するために、必要でなければ無視することができます.また、ログ機能は自分で実現すればよい.コードは次のとおりです.
    /// 
    ///       
    /// 
    public class TransactionInterceptor:IInterceptor
    {
        //        ,     
        /// 
        ///      
        /// 
        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(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空のインタフェースを定義することにより、注入するクラスはそのインタフェースを継承すればよい.コードは次のとおりです.
    /// 
    ///     IOC  
    /// 
    public class IocConfig : ConfigBase
    {
        //       
        protected override void Load(ContainerBuilder builder)
        {
            var assembly = this.GetType().GetTypeInfo().Assembly;
            builder.RegisterType();
            builder.RegisterAssemblyTypes(assembly)
                .Where(type => typeof(IDependency).IsAssignableFrom(type) && !type.GetTypeInfo().IsAbstract)
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .EnableInterfaceInterceptors()
                .InterceptedBy(typeof(TransactionInterceptor));
        }
    }

    五、例
    /// 
    ///     
    /// 
    /// 
    [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);            
    }