利用Microsoft.Practices.Unity的攔截技術,實現.NET中的AOP


1、記住這個單詞的意思:Interception(攔截)

2、首先說一下原理和背景

  原理:所謂的AOP就是面向切面編程,這里不多說,百度搜索。

  目的:個人認為是為了解耦,部分代碼跟業務代碼分離,業務代碼里面不摻雜其它功能,比如:記錄異常、記錄操作日志。

  背景:項目基本功能已完成,產品要求記錄用戶的操作日志,新增的時候記錄某人在某時做了某事(包括詳細的信息,比如新增了哪些字段或者修改了哪些字段)。於是着手在業務代碼里寫了大量的關於記錄操作日志的代碼,怎么看怎么別扭,像是被XX了的感覺。

3、解決辦法

  針對上述背景,於是想到了在業務邏輯方法上面加個特性,用以記錄操作日志,這樣代碼就變的非常干凈。而剛好項目里用到了DI,是微軟的Microsoft.Practices.Unity組件。於是在網上開始找資料,最終通過不斷的閱讀別人的代碼和反復試驗,總算實現了。因為Microsoft.Practices.Unity組件本身就自帶攔截功能。這就是為什么一開始就說攔截單詞(Interception)的原因。

  其實我是想實現這樣的代碼:  

namespace Business
{
    public interface IUserBusiness
    {
        string Speak();

        string Run();

        [OperationLog("UserName,Password,Id", "UserRepository")]
        User Create(User user, Authority authority);

        [OperationLog("", "UserRepository")]
        User Get(long id);
    }
}

  通過在方法上面添加特性,實現記錄用戶操作日志的功能。

  OperationLog特性介紹:

  第一個參數表示我想記錄哪些字段

  第二個參數表示我將采用哪個Repository來根據ID獲取原始值。

4、具體代碼

  准備好需要的Dll(2.1.505.0版本):

  Microsoft.Practices.Unity.Configuration.dll
  Microsoft.Practices.Unity.dll
  Microsoft.Practices.Unity.Interception.Configuration.dll
  Microsoft.Practices.Unity.Interception.dll

  DI我采用的是在配置文件里面做的,如下:

  unity.di.infrastructure.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <!--引用程序集-->
    <assembly name="Business" />
    <assembly name="Repository" />
    <!--引用命名空間-->
    <namespace name="Business" />
    <namespace name="Repository" />

    <container>
      <register type="IUserBusiness" mapTo="UserBusiness" />
      
      <register type="IUserRepository" mapTo="UserRepository" />
    </container>
  </unity>
</configuration>

  ioc幫助類:

namespace AopDemo
{
    /// <summary>
    /// 依賴注入幫助類
    /// 創建人:君爺
    /// 創建時間:2015-10-23
    /// </summary>
    public class IocHelper
    {
        /// <summary>
        /// 讀取接口和實現類的XML配置文件,並向MVC控制器注入自定義的ControllerFactory
        /// </summary>
        public void Init()
        {
            try
            {
                IUnityContainer container = new UnityContainer();

                //加載Ioc配置文件,讀取所有Ioc接口和實現
                var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = HttpContext.Current.Server.MapPath("~/app_data/unity.di.infrastructure.config") };
                Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
                UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection("unity");
                container.LoadConfiguration(section);
                
                //AOP
                container.AddNewExtension<Interception>();
                container.RegisterType<IUserBusiness, UserBusiness>().Configure<Interception>().SetInterceptorFor<IUserBusiness>(new InterfaceInterceptor());
                
                //向Mvc請求的上下文注入 Unity控制器工廠
                IControllerFactory controllerFactory = new UnityControllerFactory(container);
                ControllerBuilder.Current.SetControllerFactory(controllerFactory);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

    /// <summary>
    /// 依賴工廠
    /// </summary>
    public class UnityControllerFactory : DefaultControllerFactory
    {
        private readonly IUnityContainer container;

        /// <summary>
        /// 構造方法
        /// </summary>
        /// <param name="container"></param>
        public UnityControllerFactory(IUnityContainer container)
        {
            //要做異常處理            
            this.container = container;
        }

        /// <summary>
        /// 根據請求的上下文實例化控制器
        /// </summary>
        /// <param name="requestContext">請求上下文</param>
        /// <param name="controllerType">控制器類型</param>
        /// <returns></returns>
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            //這里把Controller實例注冊到了unity容器
            try
            {
                IController icontroller = container.Resolve(controllerType) as IController;
                return icontroller;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }
}

  Global.asax的Application_Start方法添加依賴注入容器

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            //IOC
            var iocFactory = new IocHelper();
            iocFactory.Init();
        }
    }

  然后添加接口記錄操作日志的Handler和Attribute

public class OperationLogHandler : ICallHandler
    {
        private string _fields;
        private string _repository;

        public OperationLogHandler(string fields, string repository)
        {
            this._fields = fields;
            this._repository = repository;
        }

        public int Order { get; set; }//這是ICallHandler的成員,表示執行順序

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            //定義存放原始值和新值的變量
            BaseModel oldValue;
            BaseModel newValue;

            //獲取類型為BaseModel的參數
            object model = null;
            object authority = null;
            foreach (var argument in input.Arguments)
            {
                if (argument.GetType().BaseType == typeof(BaseModel))
                {
                    model = argument;
                }
                else if (argument.GetType() == typeof(Authority))
                {
                    authority = argument;
                }
            }

            if (model == null)
            {
                throw new Exception("沒有找到BaseModel類型,攔截導彈失敗。");
            }

            //獲取實體的Id屬性的值
            var properties = model.GetType().GetProperties();
            var idProperty = properties.Where(m => m.Name == "Id").FirstOrDefault();
            long id = (long)idProperty.GetValue(model, null);

            //根據ID從數據庫獲取原先的值。其中Assembly.Load("Repository")中的參數是程序集的dll文件名稱,.CreateInstance()中的參數是程序集中的命名空間和類名。
            var repository = Assembly.Load("Repository").CreateInstance("Repository." + this._repository) as IRepository;
            oldValue = repository.Get(id);

            //獲取新增后返回的實體的值
            var returnValue = getNext()(input, getNext);
            newValue = returnValue.ReturnValue as BaseModel;

            //這樣就可以獲取到插入前和插入后的數據了;比較兩個實體,如果不同就記錄下來,插入日志表
            var arr = this._fields.Split(',');
            Dictionary<string, object> dic = new Dictionary<string, object>();
            foreach (var field in arr)
            {
                var property = properties.Where(m => m.Name == field).FirstOrDefault();
                var oldFieldValue = property.GetValue(oldValue, null);
                var newFieldValue = property.GetValue(newValue, null);

                var oldHashCode = oldFieldValue.GetHashCode();
                var newHashCode = newFieldValue.GetHashCode();

                if (oldHashCode != newHashCode)
                {
                    dic.Add(field, newFieldValue);
                }
            }

            return returnValue;
        }
    }
    public class OperationLogAttribute : HandlerAttribute
    {
        private string _fields;
        private string _repository;

        public OperationLogAttribute(string fields, string repository)
        {
            this._fields = fields;
            this._repository = repository;
        }

        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new OperationLogHandler(this._fields, this._repository);//返回MyHandler
        }
    }

   這樣通過配置文件和在代碼里設置AOP映射關系,我們就可以輕松的在方法上添加特性。

        [OperationLog("UserName,Password,Id", "UserRepository")]
        User Create(User user, Authority authority);

   當然中間有些其它的都省略了,關鍵是這些代碼。

  代碼之后補全


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM