原文地址:https://www.cnblogs.com/xiandnc/p/10088159.html

AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对ASP.NET程序员来说一点都不神秘,你也许早就通过Filter来完成一些通用的功能,例如你使用Authorization Filter来拦截所有的用户请求,验证Http Header中是否有合法的token。或者使用Exception Filter来处理某种特定的异常。
你之所以可以拦截所有的用户请求,能够在期望的时机来执行某些通用的行为,是因为ASP.NET
Core在框架级别预留了一些钩子,他允许你在特定的时机注入一些行为。对ASP.NET Core应用程序来说,这个时机就是HTTP请求在执行MVC
Action的中间件时。

显然这个时机并不能满足你的所有求,比如你在Repository层有一个读取数据库的方法:

1
2
3
4
public void GetUser()
{
    //Get user from db
}

你试图得到该方法执行的时间,首先想到的方式就是在整个方法外面包一层用来计算时间的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void GetUserWithTime()
{
    var stopwatch = Stopwatch.StartNew();
    try
    {
        //Get user from db
    }
    finally
    {
        stopwatch.Stop();
        Trace.WriteLine("Total" + stopwatch.ElapsedMilliseconds + "ms");
    }
}

如果仅仅是为了得到这一个方法的执行时间,这种方式可以满足你的需求。问题在于你有可能还想得到DeleteUser或者UpdateUser等方法的执行时间。修改每一个方法并添加计算时间的代码存在着明显的code smell。
一个比较优雅的做法是给需要计算时间的方法标记一个Attribute:

1
2
3
4
5
[Time]
public void GetUser()
{
    //Get user from db
}

你把计算时间这个功能当做一个切面(Aspect)注入到了现有的逻辑中,这是一个AOP的典型应用。

在C#中使用AOP

C#中可以用来做AOP的开源类库有若干个,比较流行的:

这些类库之所以能够实现AOP是因为他们有动态修改IL代码的能力,这种能力又被称为IL weaving。
还有的类库把AOP和Dependency Injection结合在了一起,通过服务上注册一个拦截器(Interceptor)的方式做达到AOP的目的,例如:

本文将使用一个C#开源项目aspect-injector来描述AOP的几种常见的场景。
aspect-injector是一个非常轻量级的AOP类库,麻雀虽小,但是已经能够应对大部分AOP的应用场景:

  • 支持.NET Core
  • 支持对异步方法注入切面
  • 能够把切面注入到方法、属性和事件上
  • 支持Attribute的方式注入切面

注入计算执行时间的逻辑

在已有的方法上注入一段逻辑可以分为三种情况:

  1. 在方法执行前注入一段逻辑,例如注入统一的认证逻辑
  2. 在方法执行后注入一段逻辑,例如将结果写入日志
  3. 方法前后同时注入逻辑,例如计算时间,又或者给整个方法内容包裹一个事务
    已知一个计算个数的方法如下:
1
2
3
4
5
6
7
8
public class SampleService
{
    public int GetCount()
    {
        Thread.Sleep(3000);
        return 10;
    }
}

为了将计算时间的逻辑包裹在现有的方法上,我们需要在被注入逻辑的方法上标记InjectAttribute

1
2
3
4
5
6
7
8
9
public class SampleService
{
    [Inject(typeof(TimeAspect))]
    public int GetCount()
    {
        Thread.Sleep(3000);
        return 10;
    }
}

TimeAspect就是我们将要注入的一个切面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Aspect(Aspect.Scope.Global)]
public class TimeAspect
  {
      [Advice(Advice.Type.Around, Advice.Target.Method)]
      public object HandleMethod(
      [Advice.Argument(Advice.Argument.Source.Name)] string name,
      [Advice.Argument(Advice.Argument.Source.Arguments)] object[] arguments,
      [Advice.Argument(Advice.Argument.Source.Target)]
      Func<object[], object> method)
      {
          Console.WriteLine($"Executing method {name}");
          var sw = Stopwatch.StartNew();
          var result = method(arguments); //调用被注入切面的方法
          sw.Stop();
          Console.WriteLine($"method {name} in {sw.ElapsedMilliseconds} ms");
          return result;
      }
  }

大部分代码是非常清晰的,我们只描述几个重要的概念:
标记了AdviceAttribute的方法就是即将要注入到目标方法的切面逻辑,也就是说HandleMethod描述了如何计算时间。
Advice.Type.Around描述了同时在目标方法的前后都注入逻辑
方法参数Func<object[], object> method其实就代表目标方法

注入认证逻辑

试想你有如果干个服务,每个服务在执行前都要做安全认证,显然安全认证的逻辑是可重用的,那我们就可以把认证的逻辑提取成一个切面(Aspect)。

1
2
3
4
5
6
7
8
9
10
11
12
13
[Inject(typeof(AuthorizationAspect))]
public class SampleService
{
    public void MethodA(Guid userId)
    {
        // Do something
    }
 
    public void MethodB(Guid userId)
    {
        // Do something
    }
}

AuthorizationAspect就是安全认证的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Aspect(Aspect.Scope.Global)]
public class AuthorizationAspect
{
    [Advice(Advice.Type.Before, Advice.Target.Method)]
    public void CheckAccess(
    [Advice.Argument(Advice.Argument.Source.Method)] MethodInfo method,
    [Advice.Argument(Advice.Argument.Source.Arguments)] object[] arguments)
    {
        if (arguments.Length == 0 || !(arguments[0] is Guid))
        {
            throw new ArgumentException($"{nameof(AuthorizationAspect)} expects
            every target method to have Guid as the first parameter");
        }
 
        var userId = (Guid)arguments[0];
        if (!_securityService.HasPermission(userId, authorizationAttr.Permission))
        {
            throw new Exception($"User {userId} doesn't have
            permission to execute method {method.Name}");
        }
    }
}

Advice.Type.Before描述了该逻辑会在被修改的方法前执行
通过object[] arguments得到了被修改方法的所有参数

AOP是面向对象编程中一种用来抽取公用逻辑,简化业务代码的方式,灵活使用AOP可以让你的业务逻辑代码不会过度臃肿,也是除了继承之外另一种可复用代码的方式。

aspect-injector地址:https://github.com/pamidur/aspect-injector/tree/master/docs#this

(转).NET Core中实现AOP编程的更多相关文章

  1. .NET Core中实现AOP编程

    AOP全称Aspect Oriented Progarmming(面向切面编程),其实AOP对ASP.NET程序员来说一点都不神秘,你也许早就通过Filter来完成一些通用的功能,例如你使用Autho ...

  2. [翻译]在 .NET Core 中的并发编程

    原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 C ...

  3. .NET Core 中的并发编程

    今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点. 然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序 ...

  4. 聊Javascript中的AOP编程

    Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...

  5. 聊聊Javascript中的AOP编程

    Duck punch 我们先不谈AOP编程,先从duck punch编程谈起. 如果你去wikipedia中查找duck punch,你查阅到的应该是monkey patch这个词条.根据解释,Mon ...

  6. 在ASP.NET Core中使用AOP来简化缓存操作

    前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...

  7. .NET下集中实现AOP编程的框架

    一.Castle 使用这个框架呢,首先是需要安装NuGet包. 先建立一个控制台项目,然后在NuGet中搜索Castle.Windsor,不出意外的话应该能找到如下的包 然后安装,会自动的安装包Cas ...

  8. [译]如何在ASP.NET Core中实现面向切面编程(AOP)

    原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...

  9. JavaEE开发之Spring中的依赖注入与AOP编程

    上篇博客我们系统的聊了<JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建>,并在之前的博客中我们聊了依赖注入的相关东西,并且使用Objective-C的R ...

随机推荐

  1. java的static和this

    1>static:静态修饰符   static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念.    被sta ...

  2. 记一次cpu指标异常的跟踪排查

    问题描述: 最近在测试环境的服务器上,无意中发现cpu持续飙高.最高的时候达到了200%经过反复重启无效之后,决定挖掘深层次的原因 通过top命令打印出消耗cpu的pid,如图 通过ps -mp 24 ...

  3. leetcode 221. 最大正方形

    题目描述: 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积. 思路分析: 一道动态规划的题.由于是正方形,首先单一的‘1’即为最小的正方形,接下来需要考察其外围区域 ...

  4. HTTP协议之multipart/form-data

    HTTP协议之multipart/form-data REST API可以用multipart/form-data,来上传文件. 在最初的 http 协议中,没有上传文件方面的功能. rfc1867为 ...

  5. js去除html标签样式

    params = params.replace(/<\/?.+?>/g,"").replace(/ /g,"");

  6. C# 序列化与反序列化之xml对属性或者字段的子类化的子对象进行序列化的解决方案

    C# 序列化与反序列化之xml对属性或者字段的子类化的子对象进行序列化的解决方案 xml序列化涉及到XmlRoot,XmlInclude,XmlElement,XmlAttribute,XmlType ...

  7. Q-Dir – 布局灵活的文件管理,强烈推荐

    Q-dir 是轻量的文件管理器,特点鲜明,各种布局视图切换灵活,默认四个小窗口组成一个大窗口,操作快捷.软件虽小,却非常好用 下载地址: https://www.softwareok.com/?Dow ...

  8. 解决pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your path问题

    解决方案: 找到python的安装路径下的pytesseract:   例如我的是  C:\develop\Python\Lib\site-packages\pytesseract .用文本编辑器打开 ...

  9. Kubernetes 清除持续 Terminating 状态的Pods

    强制删除所有Pods -- 谨慎使用 kubectl delete pods --all --grace-period=0 --force

  10. Flutter的Padding、Raw、Column、Expanded组件的基本使用

    Padding组件: Padding组件的基本使用代码: import 'package:flutter/material.dart'; import 'package:flutter_testdem ...