1. 程式人生 > >Asp.Net Core輕量級Aop解決方案:AspectCore

Asp.Net Core輕量級Aop解決方案:AspectCore

什麼是AspectCore Project ?

AspectCore Project 是適用於Asp.Net Core 平臺的輕量級 Aop(Aspect-oriented programming) 解決方案,它更好的遵循Asp.Net Core的模組化開發理念,使用AspectCore可以更容易構建低耦合、易擴充套件的Web應用程式。AspectCore使用Emit實現高效的動態代理從而不依賴任何第三方Aop庫。

開使使用AspectCore

  • 啟動 Visual Studio。從 File 選單, 選擇 New > Project。選擇 ASP.NET Core Web Application 專案模版,建立新的 ASP.NET Core Web Application 專案。

  • 從 Nuget 安裝 AspectCore.Extensions.DependencyInjection package:

    PM>   Install-Package AspectCore.Extensions.DependencyInjection
  • 在一般情況下可以使用抽象的AbstractInterceptorAttribute自定義特性類,它實現IInterceptor介面。AspectCore預設實現了基於Attribute的攔截器配置。我們的自定義攔截器看起來像下面這樣:

    public class CustomInterceptorAttribute : AbstractInterceptorAttribute 
    {
        public async override Task Invoke(AspectContext context, AspectDelegate next)
        {
            try
            {
                Console.WriteLine("Before service call");
                await next(context);
            }
            catch (Exception)
            {
                Console.WriteLine("Service threw an exception!");
                throw;
            }
            finally
            {
                Console.WriteLine("After service call");
            }
         }
     }
  • 定義ICustomService介面和它的實現類CustomService:

    public interface ICustomService
    {
        [CustomInterceptor]
        void Call();
    }
    
    public class CustomService : ICustomService
    {
        public void Call()
        {
            Console.WriteLine("service calling...");
        }
    }
  • HomeController中注入ICustomService:
    ```
    public class HomeController : Controller
    {
    private readonly ICustomService _service;
    public HomeController(ICustomService service)
    {
    _service = service;
    }

      public IActionResult Index()
      {
          _service.Call();
          return View();
      }
    }
    ```
  • 註冊ICustomService,接著,在ConfigureServices中配置建立代理型別的容器:
    public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddTransient<ICustomService, CustomService>(); services.AddMvc(); services.AddDynamicProxy(); return services.BuildAspectCoreServiceProvider(); }
  • 攔截器配置。

    全域性攔截器。使用AddDynamicProxy(Action<IAspectConfiguration>)的過載方法,其中IAspectConfiguration提供Interceptors註冊全域性攔截器:

     services.AddDynamicProxy(config =>
     {
          config.Interceptors.AddTyped<CustomInterceptorAttribute>();
     });

    帶構造器引數的全域性攔截器,在CustomInterceptorAttribute中新增帶引數的構造器:

    public class CustomInterceptorAttribute : AbstractInterceptorAttribute 
    {
        private readonly string _name;
        public CustomInterceptorAttribute(string name)
        {
            _name = name;
        }
        public async override Task Invoke(AspectContext context, AspectDelegate next)
        {
            try
            {
                Console.WriteLine("Before service call");
                await next(context);
            }
            catch (Exception)
            {
                Console.WriteLine("Service threw an exception!");
                throw;
            }
            finally
            {
                Console.WriteLine("After service call");
            }
        }
    }

    修改全域性攔截器註冊:

    services.AddDynamicProxy(config =>
    {
         config.Interceptors.AddTyped<CustomInterceptorAttribute>(args: new object[] { "custom" });
    });

    作為服務的全域性攔截器。在ConfigureServices中新增:

    services.AddTransient<CustomInterceptorAttribute>(provider => new CustomInterceptorAttribute("service"));

    修改全域性攔截器註冊:

    services.AddDynamicProxy(config =>
    {
        config.Interceptors.AddServiced<CustomInterceptorAttribute>();
    });

    作用於特定ServiceMethod的全域性攔截器,下面的程式碼演示了作用於帶有Service字尾的類的全域性攔截器:

    services.AddDynamicProxy(config =>
    {
        config.Interceptors.AddTyped<CustomInterceptorAttribute>(method => method.DeclaringType.Name.EndsWith("Service"));
    });
    使用萬用字元的特定全域性攔截器:
    services.AddDynamicProxy(config => { config.Interceptors.AddTyped<CustomInterceptorAttribute>(Predicates.ForService("*Service")); });
  • 在AspectCore中提供NonAspectAttribute來使得ServiceMethod不被代理:

    [NonAspect]
    public interface ICustomService
    {
        void Call();
    }

    同時支援全域性忽略配置,亦支援萬用字元:

     services.AddDynamicProxy(config =>
      {
          //App1名稱空間下的Service不會被代理
          config.NonAspectPredicates.AddNamespace("App1");
    
          //最後一級為App1的名稱空間下的Service不會被代理
          config.NonAspectPredicates.AddNamespace("*.App1");
    
          //ICustomService介面不會被代理
          config.NonAspectPredicates.AddService("ICustomService");
    
          //字尾為Service的介面和類不會被代理
          config.NonAspectPredicates.AddService("*Service");
    
          //命名為Query的方法不會被代理
          config.NonAspectPredicates.AddMethod("Query");
    
          //字尾為Query的方法不會被代理
          config.NonAspectPredicates.AddMethod("*Query");
      });
    
  • 攔截器中的依賴注入。在攔截器中支援屬性注入,構造器注入和服務定位器模式。
    屬性注入,在攔截器中擁有public get and set許可權的屬性標記[AspectCore.Injector.FromContainerAttribute]特性,即可自動注入該屬性,如:

    public class CustomInterceptorAttribute : AbstractInterceptorAttribute 
    {
        [FromContainer]
        public ILogger<CustomInterceptorAttribute> Logger { get; set; }
    
    
        public override Task Invoke(AspectContext context, AspectDelegate next)
        {
            Logger.LogInformation("call interceptor");
            return next(context);
        }
    }

    構造器注入需要使攔截器作為Service,除全域性攔截器外,仍可使用ServiceInterceptor使攔截器從DI中啟用:

    public interface ICustomService
    {
        [ServiceInterceptor(typeof(CustomInterceptorAttribute))]
        void Call();
    }

    服務定位器模式。攔截器上下文AspectContext可以獲取當前Scoped的ServiceProvider

    public class CustomInterceptorAttribute : AbstractInterceptorAttribute 
    {
        public override Task Invoke(AspectContext context, AspectDelegate next)
        {
            var logger = context.ServiceProvider.GetService<ILogger<CustomInterceptorAttribute>>();
            logger.LogInformation("call interceptor");
            return next(context);
        }
    }

有問題反饋