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的更多相关文章

  1. 第九回 Microsoft.Practices.Unity.Interception实现基于数据集的缓存(针对六,七,八讲的具体概念和配置的解说)

    返回目录 概念 Microsoft.Practices.Unity.Interception是一个拦截器,它隶属于Microsoft.Practices.Unity组成之中,主要完成AOP的功能,而实 ...

  2. Microsoft.Practices.Unity 给不同的对象注入不同的Logger

    场景:我们做项目的时候常常会引用第三方日志框架来帮助我们记录日志,日志组件的用途主要是审计.跟踪.和调试.就说我最常用的日志组件log4net吧,这个在.NET同行当中应该算是用得非常多的一个日志组件 ...

  3. WPF Microsoft.Practices.Unity 注入大法简单示例

    最近新入职了公司,做WPF方向的项目,进来后看到这边大量运用了依赖注入来解耦,采用的是Microsoft.Practices.Unity. WPF的话,目前主要有两个技术来实现IOC,unity和ME ...

  4. Microsoft.Practices.Unity

    // // Summary: // Register a type mapping with the container. // // Parameters: // container: // Con ...

  5. VS - Microsoft.Practices.Unity

    PM>  Install-Package Unity Web.config <configSections> <section name="unity" t ...

  6. 使用Microsoft.Practices.Unity 依赖注入

    Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...

  7. 使用Microsoft.Practices.Unity 依赖注入 转载https://www.cnblogs.com/slardar1978/p/4205394.html

    Unity是微软Patterns & Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(Dependency Injection)容器,它支持常用的三种依赖注入方式:构造器注入 ...

  8. 20190703_创建 unity 的配置节处理程序时出错: The type name or alias Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension

    创建 unity 的配置节处理程序时出错: The type name or alias Microsoft.Practices.Unity.InterceptionExtension.Configu ...

  9. 利用多态,实现一般处理程序(ashx)中的AOP(切面编程)

    本文是对工作中的项目进行代码优化(完善登陆验证的AOP切面编程)时,所遇到的各种解决方案思考过程. 项目背景:由ashx+nvelocity构建的简单B/S问卷系统,现需要优化登录验证环节(时隔若干个 ...

随机推荐

  1. SharePoint服务器端对象模型 之 使用CAML进展数据查询

    SharePoint服务器端对象模型 之 使用CAML进行数据查询 一.概述 在SharePoint的开发应用中,查询是非常常用的一种手段,根据某些筛选.排序条件,获得某个列表或者某一些列表中相应的列 ...

  2. collection view 开发笔记

    使用collectionView 注册的cell 不会调用 init 方法 会调用 initwith fram 方法.

  3. XML总结

    1. XML 基本概念 扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自 ...

  4. Database 'xxx' cannot be upgraded because it is read-only or has read-only file Make the database or files writeable, and rerun recovery.

      在分离数据库DatabaseName(暂且用DatabaseName代替该数据库名)后,我将其数据文件以及日志文件移动到新增的磁盘上.然后附加该数据库,结果报如下错误: Database 'Dat ...

  5. Failed to retrieve data for this request. (Microsoft.SqlServer.Management.Sdk.Sfc)

    使用Microsoft SQL SERVER 2014 Management Studio访问Azure SQL Database时,查看存储过程时遇到下面错误信息: TITLE: Microsoft ...

  6. URL无法显示某些特殊符号

    URL无法显示某些特殊符号,这个时候就要使用编码了.编码的格式为:一个百分号,后面跟对应字符的ASCII(16进制)码值.例如 空格的编码值是"%20".(ASCII参考)URL中 ...

  7. MYSQL介绍安装及一些问题解决

    一.简介 MySQL是最流行的开放源码SQL数据库管理系统,它是由MySQL AB公司开发.发布并支持的.有以下特点: MySQL是一种数据库管理系统. MySQL是一种关联数据库管理系统. MySQ ...

  8. C++ string类的实现

    c++中string类的实现 今天面试被考到了, 全给忘记了!!!   //string类的实现 #include <iostream> #include <string.h> ...

  9. 使用Tmux提高linux终端环境下的效率

    最近转移到linux下开发,同事告诉我一个工具tmux.关于tmux的工具的使用参考以下文章 如何使用Tmux提高终端环境下的效率 tmux的使用和快捷键 tmux简明教程 tmux使用大全

  10. Linux下TomcatVM参数修改:Native memory allocation (mmap) failed to map 3221225472 bytes for committing reserved memory.

    不可行的方法最初我直接修改catalina.sh, 将JAVA_OPTS变量加上了 -server -Xms1G -Xmx1G -XX:+UserG1GC最初看起来没啥问题,但是当服务器运行几天后,发 ...