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. 参加了iDOF2016会议,发表演讲“油田SOA与云平台的系统思考与实践”

    PPT的全部抓图,扫描二维码直接到微信里去看吧:

  2. Android M Permission 运行时权限 学习笔记

    Android M Permission 运行时权限 学习笔记 从Android 6.0开始, 用户需要在运行时请求权限, 本文对运行时权限的申请和处理进行介绍, 并讨论了使用运行时权限时新老版本的一 ...

  3. databtables 设置(显示)行号

    var table = $('#priceStrategtyTable').DataTable({         "rowCallback": function( row, da ...

  4. Git:Git初体验——Git安装配置

    作为即将成为一个程序员的男人,一直在听别人说Git多好多好,之前也随便了解了一些,但是始终没有决心去学会.现在大四了,只有毕设和一门开学六七周只去过一次课的全员必修课外,也没有什么事情做,何不去做这些 ...

  5. yii2批量添加的问题

    作者:白狼 出处:http://www.manks.top/yii2_batch_insert.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否 ...

  6. 下一代Asp.net开发规范OWIN(2)—— Katana介绍以及使用

    接上篇OWIN产生的背景以及简单介绍,在了解了OWIN规范的来龙去脉后,接下来看一下Katana这个OWIN规范的实现,并看看如何使用在我们的Web开发中. 阅读目录: 一. Katana项目的结构和 ...

  7. Html--表单练习

          <!--文档定义一定要带上,因为浏览器在解析的时候先按照文档定义的格式解析,   如果没有就按照浏览器默认的格式解析,可能会出问题.-->   <html>   & ...

  8. ORACLE临时表空间总结

    临时表空间概念 临时表空间用来管理数据库排序操作以及用于存储临时表.中间排序结果等临时对象,当ORACLE里需要用到SORT的时候,并且当PGA中sort_area_size大小不够时,将会把数据放入 ...

  9. python之数据库操作

    数据库操作 Python 操作 Mysql 模块的安装 1 2 3 4 5 linux:     yum install MySQL-python   window:     http://files ...

  10. mysql慢查询日志分析工具 mysqlsla(转)

    mysql数据库的慢查询日志是非常重要的一项调优辅助日志,但是mysql默认记录的日志格式阅读时不够友好,这是由mysql日志记录规则所决定的,捕获一条就记录一条,虽说记录的信息足够详尽,但如果将浏览 ...