Castle DynamicProxy基本用法(AOP)
本文介绍AOP编程的基本概念、Castle DynamicProxy(DP)的基本用法,使用第三方扩展实现对异步(async)的支持,结合Autofac演示如何实现AOP编程。 AOP百科中关于AOP的解释:
在AOP中,我们关注横切点,将通用的处理流程提取出来,我们会提供系统通用功能,并在各业务层中进行使用,例如日志模块、异常处理模块等。通过AOP编程实现更加灵活高效的开发体验。 DynamicProxy的基本用法动态代理是实现AOP的一种方式,即在开发过程中我们不需要处理切面中(日志等)的工作,而是在运行时,通过动态代理来自动完成。Castle DynamicProxy是一个实现动态代理的框架,被很多优秀的项目用来实现AOP编程,EF Core、Autofac等。 我们来看两段代码,演示AOP的好处。在使用AOP之前: public class ProductRepository : IProductRepository { private readonly ILogger logger; public ProductRepository(ILogger logger) { this.logger = logger; } public void Update(Product product) { //执行更新操作 //...... //记录日志 logger.WriteLog($"产品{product}已更新"); } } 在使用AOP之后: public class ProductRepository : IProductRepository { public void Update(Product product) { //执行更新操作 //...... } } 可以明显的看出,在使用之前我们的ProductRepository依赖于ILogger,并在执行Update操作以后,要写出记录日志的代码;而在使用之后,将日志记录交给动态代理来处理,降低了不少的开发量,即使遇见略微马虎的程序员,也不耽误我们日志的记录。 那该如何实现这样的操作呢?
public class LoggerInterceptor : IInterceptor { private readonly ILogger logger; public LoggerInterceptor(ILogger logger) { this.logger = logger; } public void Intercept(IInvocation invocation) { //获取执行信息 var methodName = invocation.Method.Name; //调用业务方法 invocation.Proceed(); //记录日志 this.logger.WriteLog($"{methodName} 已执行"); } }
static void Main(string[] args) { ILogger logger = new ConsoleLogger(); Product product = new Product() { Name = "Book" }; IProductRepository target = new ProductRepository(); ProxyGenerator generator = new ProxyGenerator(); IInterceptor loggerIntercept = new LoggerInterceptor(logger); IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(target,loggerIntercept); proxy.Update(product); } 至此,我们已经完成了一个日志拦截器,其它业务需要用到日志记录的时候,也可通过创建动态代理的方式来进行AOP编程。 但是,调用起来还是比较复杂,需要怎么改进呢?当然是使用依赖注入(DI)了。 Autofac的集成Autofac集成了对DynamicProxy的支持,我们需要引用 ContainerBuilder builder = new ContainerBuilder(); //注册拦截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); //注册基础服务 builder.RegisterType<ConsoleLogger>().AsImplementedInterfaces(); //注册要拦截的服务 builder.RegisterType<ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //启用接口拦截 .InterceptedBy(typeof(LoggerInterceptor)); //指定拦截器 var container = builder.Build(); //解析服务 var productRepository = container.Resolve<IProductRepository>(); Product product = new Product() { Name = "Book" }; productRepository.Update(product); 对这段代码做一下说明:
DynamicProxy的基本原理上面我们说到动态代理只对公共接口方法、类中的虚方法生效,你是否想过为什么? 其实,动态代理是在运行时为我们动态生成了一个代理类,通过 如果不使用动态代理,我们的代理服务应该是什么样的呢?来看下面的代码,让我们手工创建一个代理类: 以下是我对代理类的理解,请大家辩证的看待,如果存在不正确的地方,还望指出。 为接口使用代理: public class IProductRepositoryProxy : IProductRepository { private readonly ILogger logger; private readonly IProductRepository target; public ProductRepositoryProxy(ILogger logger,IProductRepository target) { this.logger = logger; this.target = target; } public void Update(Product product) { //调用IProductRepository的Update操作 target.Update(product); //记录日志 this.logger.WriteLog($"{nameof(Update)} 已执行"); } } //使用代理类 IProductRepository target = new ProductRepository(); ILogger logger = new ConsoleLogger(); IProductRepository productRepository = new ProductRepositoryProxy(logger,target); 为类使用接口: public class ProductRepository : IProductRepository { //改写为虚方法 public virtual void Update(Product product) { //执行更新操作 //...... } } public class ProductRepositoryProxy : ProductRepository { private readonly ILogger logger; public ProductRepositoryProxy(ILogger logger) { this.logger = logger; } public override void Update(Product product) { //调用父类的Update操作 base.Update(product); //记录日志 this.logger.WriteLog($"{nameof(Update)} 已执行"); } } //使用代理类 ILogger logger = new ConsoleLogger(); ProductRepository productRepository = new ProductRepositoryProxy(logger); 异步(async/await)的支持如果你站在应用程序的角度来看,异步只是微软的一个语法糖,使用异步的方法返回结果为一个Task或Task
我们本节仍然结合Autofac进行处理,首先对代码进行改造,将 public class ProductRepository : IProductRepository { public virtual Task<int> Update(Product product) { Console.WriteLine($"{nameof(Update)} Entry"); //执行更新操作 var task = Task.Run(() => { //...... Thread.Sleep(1000); Console.WriteLine($"{nameof(Update)} 更新操作已完成"); //返回执行结果 return 1; }); //返回 return task; } } 接下来定义我们的异步拦截器: public class LoggerAsyncInterceptor : IAsyncInterceptor { private readonly ILogger logger; public LoggerAsyncInterceptor(ILogger logger) { this.logger = logger; } /// <summary> /// 同步方法拦截时使用 /// </summary> /// <param name="invocation"></param> public void InterceptSynchronous(IInvocation invocation) { throw new NotImplementedException(); } /// <summary> /// 异步方法返回Task时使用 /// </summary> /// <param name="invocation"></param> public void InterceptAsynchronous(IInvocation invocation) { throw new NotImplementedException(); } /// <summary> /// 异步方法返回Task<T>时使用 /// </summary> /// <typeparam name="TResult"></typeparam> /// <param name="invocation"></param> public void InterceptAsynchronous<TResult>(IInvocation invocation) { //调用业务方法 invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation); } private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation) { //获取执行信息 var methodName = invocation.Method.Name; invocation.Proceed(); var task = (Task<TResult>)invocation.ReturnValue; TResult result = await task; //记录日志 this.logger.WriteLog($"{methodName} 已执行,返回结果:{result}"); return result; } }
在我们上面的代码中,只实现了 由于 public class LoggerInterceptor : IInterceptor { private readonly LoggerAsyncInterceptor interceptor; public LoggerInterceptor(LoggerAsyncInterceptor interceptor) { this.interceptor = interceptor; } public void Intercept(IInvocation invocation) { this.interceptor.ToInterceptor().Intercept(invocation); } } 从代码中可以看到,异步拦截器 接下来我们修改DI的服务注册部分: ContainerBuilder builder = new ContainerBuilder(); //注册拦截器 builder.RegisterType<LoggerInterceptor>().AsSelf(); builder.RegisterType<LoggerAsyncInterceptor>().AsSelf(); //注册基础服务 builder.RegisterType<ConsoleLogger>().AsImplementedInterfaces(); //注册要拦截的服务 builder.RegisterType<ProductRepository>().AsImplementedInterfaces() .EnableInterfaceInterceptors() //启用接口拦截 .InterceptedBy(typeof(LoggerInterceptor)); //指定拦截器 var container = builder.Build(); 以上便是通过 探讨:ASP.NET MVC中的切面编程通过上面的介绍,我们已经了解了AOP的基本用法,但是如何用在
我们知道,AOP的初衷就是对使用者保持黑盒,通过抽取切面进行编程,而这两个问题恰恰需要我们对使用者进行修改,违背了SOLID原则。 那么,如果我们要在MVC中使用AOP,有什么方法呢?其实MVC已经为我们提供了两种实现AOP的方式:
这两种方式更符合我们的编码习惯,也体现了MVC框架的特性。 综上,不建议在MVC中对Controller使用DP。如果采用NLayer架构,则可以在Application层、Domain层使用DP,来实现类似数据审计、SQL跟踪等处理。 虽然不推荐,但还是给出代码,给自己多一条路:
services.AddMvc() .AddControllersAsServices();
builder.RegisterType<ProductController>() .EnableClassInterceptors() .InterceptedBy(typeof(ControllerInterceptor));
[HttpPost] public virtual Task<int> Update(Product product) { return this.productRepository.Update(product); } 参考文档:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – MetadataTypeAttribute在DNX Core 5.0中不可
- asp.net-mvc – ASP.NET MVC自定义成员资格提供程序Web.con
- asp.net-mvc – ASP.NET MVC NHibernate模型绑定
- asp.net-mvc – 通过子域名到mvc或api的ASP.NET路由
- asp.net-mvc – 表单帖子永远不会执行操作,并且在MVC 6 RC2
- asp.net-mvc – ASP.NET MVC框架中的MVVM和ModelBinders
- asp.net-mvc – 如何在.NET MVC3 HTML表单中的必需可空Date
- asp.net-mvc – MVC3 Html.HiddenFor(Model => Model.Id)不
- asp.net-mvc – ASP.NET MVC ViewData和视图模型的最佳实践
- asp.net – 在.NET Core 1.0 MVC中的视图中使用授权策略的任
- asp.net signalR 专题—— 第三篇 如何从外部线程
- asp.net – 如何将AD身份验证SSO与现有的Forms身
- 如何在ASP.NET MVC中使用JQuery调用控制器操作
- asp.net核心 – 如何在自己的主机环境中的ASP.NE
- [.Net码农]ASP.NET MVC 之 View 测试
- 如何更新asp.net网站(刚更改的文件)
- asp.net – Visual Studio 2012 – 哪里有ASPX设
- asp.net – VS 2012发布:找不到有效的AspnetMer
- asp.net – 使用installshield在安装后运行解决方
- 关于Type Initializer和 BeforeFieldInit的问题,