利用Microsoft.Practices.Unity的拦截技术,实现.NET中的AOP

摘要:
DI、微软。实践。微软的Unity组件恰好用于该项目。因为微软。实践。Unity组件本身具有拦截功能。这就是为什么我一开始就说“封锁词”。

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);

   当然中间有些其它的都省略了,关键是这些代码。

  代码之后补全

免责声明:文章转载自《利用Microsoft.Practices.Unity的拦截技术,实现.NET中的AOP》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇图像放大算法mysql 删除重复数据只保留一条记录下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#.NET 中的类型转换

C# 出来也有些日子了,最近由于编程的需要,对 C# 的类型转换做了一些研究,其内容涉及 C# 的装箱/拆箱/别名、数值类型间相互转换、字符的 ASCII 码和 Unicode 码、数值字符串和数值之间的转换、字符串和字符数组/字节数组之间的转换、各种数值类型和字节数组之间的转换、十六进制数输出以及日期型数据的一些转换处理,在这里与大家分享—— 1. 装箱...

Scala入门系列(十一):模式匹配

引言 模式匹配是Scala中非常有特色,非常强大的一种功能。 类似于Java中的switch case语法,但是模式匹配的功能要比它强大得多,switch只能对值进行匹配,但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配甚至对有值或没值(Option)进行匹配...

EF性能优化

十年河东,十年河西,莫欺少年穷。 EF就如同那个少年,ADO.NET则是一位壮年。毕竟ADO.NET出生在EF之前,而EF所走的路属于应用ADO.NET。 也就是说:你所写的LINQ查询,最后还是要转化为ADO.NET的SQL语句,转化过程中无形降低了EF的执行效率。 但是,使用EF的一个好处就是系统便于维护,减少了系统开发时间,降低了生成成本。 OK,上...

JAVA缓存技术之EhCache(转)

最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考。此为转贴,帖子来处:http://cogipard.info/articles/cache-static-files-with-jnotify-and-ehcache介绍 JNotify:http://jnotify.sou...

HashMap实现缓存

package com.cache; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; pub...

Android 下的EXIF

一.什么是Exif Exif(Exchangeable Image File 可交换图像文件)是一种图象文件格式,它的数据存储与JPEG格式是完全相同的。实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括拍 摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及全球定位系统(GPS...