[译]如何在ASP.NET Core中实现面向切面编程(AOP)
原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE
原文作者:ZANID HAYTAM
译文地址:如何在ASP.NET Core中实现面向切面编程(AOP)
译文作者:Lamond Lu
前言
在使用了Spring Boot数月之后, 我发觉ASP.NET Core中缺失了对面向切面编程(AOP)的默认支持。
维基百科中针对AOP的定义:
面向切面编程(AOP)是一种编程范例,其旨在通过允许跨领域关注点的分离来提高模块化。它通过“切入点”规范指定要修改的代码,不修改源代码本身的情况下,向现有代码提供额外行为,例如使用日志的方式记录为所有以"set"开头的方法调用记录。使用该方式,你可以向核心业务逻辑中追加一些不太重要的功能(例如日志),而不会使代码混乱。AOP为面向切换的软件开发奠定了基础。
以下是AOP的一些常用场景
- 日志审计
- 事务管理
- 安全
代理模式(Proxy Pattern)也常用于Mocking(例如Moq, NSubstitute等)和延时加载(Lazy Loading)(例如EF Core, NHierante等)
C#中实现AOP
C#中其实已经支持AOP了,你可以快速Google搜索一下,AOP的实现方式有2种RealProxy
真实代理和MarshalByRefObject
.技术上讲,他们都可以在本地和远程使用,它看起来非常的美好,直到你明白的你的所有目标对象都必须继承MarshalByRefObject
。仅此一点,就让大部分人不会考虑这种实现方式。
库更好的实现方式
幸运的是,我们在C#中可以使用一种更好的方式创建代理对象,即使用Castle.DynamicProxy
库。
Castle.DynamicProxy
是一个用于在运行时生成轻量级.NET代理的库。生成代理对象允许你在不修改原始代码的情况下拦截对对象成员的调用,只有virtual对象成员才能被拦截。 - Castle Project
使用Castle提供的动态代理,你可以为抽象类、接口(同时提供实现)以及带有virtual方法/属性的普通类创建代理对象。
以下是一个例子,这里我们假设创建了一个处理博客文章的服务应用。
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool Disabled { get; set; }
public DateTime Created { get; set; }
}
public interface IBlogService
{
void DisablePost(BlogPost post);
BlogPost GetPost(int id);
}
public class BlogService : IBlogService
{
public BlogPost GetPost(int id)
{
return new BlogPost
{
Id = id,
Title = "Test",
Description = "Test",
Disabled = false,
Created = DateTime.UtcNow
};
}
public void DisablePost(BlogPost post)
{
post.Disabled = true;
}
}
通常,你会将BlogService
类注册为IBlogService
接口的实现,一切都运转的非常正常。但是现在,你希望代理这个接口,当接口中任何方法被调用的时候,做点什么事情。
这里,我们首先创建一个拦截器对象以便拦截方法调用,就像RealProxy
一样
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"正在调用方法 {invocation.TargetType}.{invocation.Method.Name}.");
invocation.Proceed(); // 执行当前被拦截的方法
}
}
然后,我们将使用一个代理生成器生成代理对象。
var generator = new ProxyGenerator();
var actual = new BlogService();
var proxiedService = (IBlogService)proxyGenerator.CreateInterfaceProxyWithTarget(typeof(IBlogService), actual, new LoggingInterceptor());
// 使用proxiedService对象和你平常使用IBlogService对象是一样的
现在我们就创建出了一个实现了IBlogService
接口的代理对象,其中包含了内部实现BlogService
。当任何一个接口方法被调用的时候,LoggingInterceptor.Intercept
方法就会被调用,当拦截器调用invocation.Proceed()
方法时,它在BlogService
类中的具体实现方法就会被调用。
如何在ASP.NET Core中使用Castle实现AOP
在ASP.NET Core中使用Castle实现AOP的实现思路是, 始终使用ASP.NET Core的IOC容器来创建代理服务。虽然Castle项目中包含它自己的IOC容器Castle Windor
, 使得注入代理更加的容易,但是我们暂时不使用它。
这里,我们首先为我们的LoggingInterceptor
添加一个简单的依赖以展示我们如何使用ASP.NET Core自带的DI来处理依赖问题。因为现实中,你的大部分拦截器都是需要一个或多个依赖项的。
public class LoggingInterceptor : IInterceptor
{
private readonly ILogger<LoggingInterceptor> _logger;
public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
{
_logger = logger;
}
public void Intercept(IInvocation invocation)
{
_logger.LogDebug($"Calling method {invocation.TargetType}.{invocation.Method.Name}.");
invocation.Proceed();
}
}
第二步,我们在依赖注入容器中注册一个单例的ProxyGenerator
对象,以及我们即将使用的所有的拦截器对象
services.AddSingleton(new ProxyGenerator());
services.AddScoped<IInterceptor, LoggingInterceptor>();
最后,我们创建一个扩展方法AddProxiedScoped
, 并使用它注册其他所有服务。
public static class ServicesExtensions
{
public static void AddProxiedScoped<TInterface, TImplementation>(this IServiceCollection services)
where TInterface : class
where TImplementation : class, TInterface
{
services.AddScoped<TImplementation>();
services.AddScoped(typeof(TInterface), serviceProvider =>
{
var proxyGenerator = serviceProvider.GetRequiredService<ProxyGenerator>();
var actual = serviceProvider.GetRequiredService<TImplementation>();
var interceptors = serviceProvider.GetServices<IInterceptor>().ToArray();
return proxyGenerator.CreateInterfaceProxyWithTarget(typeof(TInterface), actual, interceptors);
});
}
}
// In ConfigureServices
services.AddProxiedScoped<IBlogService, BlogService>();
这里,让我们看看它是如何工作的
- 我们注册具体实现(例如
BlogService
)。这是因为具体实现可能也需要使用依赖注入容器解决依赖问题。 - 每当从依赖注入容器中尝试获取接口对象的时候:
- 我们取得了一个
ProxyGenerator
对象的实例 - 我们获得了一个接口的实现实例
- 我们获取到了所有注册的拦截器
- 使用代理生成器创建接口的代理对象,这个对象中包含了一个具体实现和其使用的拦截器。
- 我们取得了一个
现在,我们无论何时需要一个IBlogService
接口对象,都可以通过依赖注入容器得到一个代理对象,这个代理对象会先经过所有的拦截器,然后调用BlogService
中定义的实际方法。
但是这里,相较与Spring
,在ASP.NET Core中实现AOP还不够简单直接,但是我们可以轻松将其转换为简单的“框架”,我们可以使用Castle.DynamicProxy
的一些特定方法,来执行一些更高级的操作。
希望本篇文章对你有所帮助,Happy Coding!
[译]如何在ASP.NET Core中实现面向切面编程(AOP)的更多相关文章
- Spring中的面向切面编程(AOP)简介
一.什么是AOP AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面 ...
- 如何在ASP.NET Core中实现CORS跨域
注:下载本文的完整代码示例请访问 > How to enable CORS(Cross-origin resource sharing) in ASP.NET Core 如何在ASP.NET C ...
- 如何在ASP.NET Core中实现一个基础的身份认证
注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...
- 如何在ASP.NET Core中应用Entity Framework
注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...
- [转]如何在ASP.NET Core中实现一个基础的身份认证
本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...
- 如何在ASP.NET Core中使用Azure Service Bus Queue
原文:USING AZURE SERVICE BUS QUEUES WITH ASP.NET CORE SERVICES 作者:damienbod 译文:如何在ASP.NET Core中使用Azure ...
- 如何在ASP.NET Core中自定义Azure Storage File Provider
文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...
- 如何在ASP.NET Core中使用JSON Patch
原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...
- [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置
[翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置 原文: USING CONSUL FOR STORING THE CONFIGURATION IN ASP.NET COR ...
随机推荐
- 改变对象的字符串显示__str__repr
改变对象的字符串显示 # l=list('hello') # # print(l) # file=open('test.txt','w') # print(file) class Foo: def _ ...
- 【Python 实例】回文数判断
[Python 实例]回文数判断 题目: 源代码: 运行结果: 题目: 判断输入的字符串是否为回文数 源代码: """ string_reverse_output():反 ...
- Java中增强一个类的几种方法
今天有人问我怎么增强一个类的功能.博客刚好没东西,今天就讲讲增强类. 增强的手段有三种类型: 1.继承或者实现接口:特点是被增强对象不能变,增强的内容不能变. 2.装饰着模式:特点是被增强对象可变,但 ...
- Window Server2012 修改远程桌面端口号
Win + R 输入 regedit 打开注册表编辑器 在注册表编辑器中找到 PortNumber 双击 PortNumber,选择10进制,修改想要的端口号 把修改的端口添加为入站规则 重启 Rem ...
- ECS7天实践进阶训练营Day1:使用阿里云ECS,快速搭建、管理VuePress静态网站
一.概述 VuePress是2018年由尤雨溪发布的一个全新的基于Vue的静态网站生成器,它是一个非常轻量级的静态网站生成器.VuePress主要用于生成技术文档,其类似于Gitbook,我们可以用于 ...
- ALGEBRA-1 向量空间
向量空间对加法封闭 对数乘封闭 直和:表示的唯一性
- 2020-06-11:Redis支持的数据类型?
福哥答案2020-06-11: 福哥口诀法:字哈列集有(string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合))位超地流(位图bitma ...
- P6087 [JSOI2015]送礼物 01分数规划+单调队列+ST表
P6087 [JSOI2015]送礼物 01分数规划+单调队列+ST表 题目背景 \(JYY\) 和 \(CX\) 的结婚纪念日即将到来,\(JYY\) 来到萌萌开的礼品店选购纪念礼物. 萌萌的礼品店 ...
- Java中的注解及自定义注解你用的怎么样,能不能像我这样应用自如?
Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...
- 学习Python(新手教程链接)
1.这个是地址: https://www.ggdoc.com/cHl0aG9uIG1zaeaYr_S7gOS5iA2/NTY4Nzc1MWQxMDJkZTJiZDk3MDU4OGE20/