AspectCore Project 是適用於Asp.Net Core 平台的輕量級 Aop(Aspect-oriented programming) 解決方案,它更好的遵循Asp.Net Core的模組化開發理念,使用AspectCore可以更容易構建低耦合、易擴充的Web應用程式。AspectCore使用Emit實現高效的動態代理從而不依賴任何第三方Aop庫。
啟動 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
在一般情況下可以使用抽象的InterceptorAttribute
自訂屬性類別,它實現IInterceptor
介面。AspectCore預設實現了基於Attribute
的攔截器配置。我們的自訂攔截器看起來像下面這樣:
public class CustomInterceptorAttribute : InterceptorAttribute{ public async override Task Invoke(IAspectContext 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.AddAspectCore(); return services.BuildAspectCoreServiceProvider();}
攔截器配置。首先安裝AspectCore.Extensions.Configuration
package:
PM> Install-Package AspectCore.Extensions.Configuration
全域攔截器。使用AddAspectCore(Action<AspectCoreOptions>)
的重載方法,其中AspectCoreOptions
提供InterceptorFactories
註冊全域攔截器:
services.AddAspectCore(config => { config.InterceptorFactories.AddTyped<CustomInterceptorAttribute>(); });
帶構造器參數的全域攔截器,在CustomInterceptorAttribute
中添加帶參數的構造器:
public class CustomInterceptorAttribute : InterceptorAttribute{ 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.AddAspectCore(config =>{ config.InterceptorFactories.AddTyped<CustomInterceptorAttribute>(args: new object[] { "custom" });});
作為服務的全域攔截器。在ConfigureServices
中添加:
services.AddTransient<CustomInterceptorAttribute>(provider => new CustomInterceptorAttribute("service"));
修改全域攔截器註冊:
services.AddAspectCore(config =>{ config.InterceptorFactories.AddServiced<CustomInterceptorAttribute>();});
作用於特定Service
或Method
的全域攔截器,下面的代碼示範了作用於帶有Service
尾碼的類的全域攔截器:
services.AddAspectCore(config =>{ config.InterceptorFactories.AddTyped<CustomInterceptorAttribute>(method => method.DeclaringType.Name.EndsWith("Service"));});
使用萬用字元的特定全域攔截器:
services.AddAspectCore(config =>{ config.InterceptorFactories.AddTyped<CustomInterceptorAttribute>(PredicateFactory.ForService("*Service"));});
在AspectCore中提供NonAspectAttribute
來使得Service
或Method
不被代理:
[NonAspect]public interface ICustomService{ void Call();}
同時支援全域忽略配置,亦支援萬用字元:
services.AddAspectCore(config => { //App1命名空間下的Service不會被代理 config.NonAspectOptions.AddNamespace("App1"); //最後一級為App1的命名空間下的Service不會被代理 config.NonAspectOptions.AddNamespace("*.App1"); //ICustomService介面不會被代理 config.NonAspectOptions.AddService("ICustomService"); //尾碼為Service的介面和類不會被代理 config.NonAspectOptions.AddService("*Service"); //命名為Query的方法不會被代理 config.NonAspectOptions.AddMethod("Query"); //尾碼為Query的方法不會被代理 config.NonAspectOptions.AddMethod("*Query"); });
攔截器中的依賴注入。在攔截器中支援屬性注入,構造器注入和服務定位器模式。
屬性注入,在攔截器中擁有public get and set
許可權的屬性標記[AspectCore.Abstractions.FromServices]
(區別於Microsoft.AspNetCore.Mvc.FromServices
)特性,即可自動注入該屬性,如:
public class CustomInterceptorAttribute : InterceptorAttribute{ [AspectCore.Abstractions.FromServices] 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 : InterceptorAttribute{ public override Task Invoke(AspectContext context, AspectDelegate next) { var logger = context.ServiceProvider.GetService<ILogger<CustomInterceptorAttribute>>(); logger.LogInformation("call interceptor"); return next(context); }}
使用Autofac
和AspectCore
。AspectCore原生支援整合Autofac,我們需要安裝下面兩個nuget packages:
PM> Install-Package Autofac.Extensions.DependencyInjectionPM> Install-Package AspectCore.Extensions.Autofac
AspectCore提供RegisterAspectCore
擴充方法在Autofac的Container
中註冊動態代理需要的服務,並提供AsInterfacesProxy
和AsClassProxy
擴充方法啟用interface和class的代理。修改ConfigureServices
方法為:
public IServiceProvider ConfigureServices(IServiceCollection services){ services.AddMvc(); var container = new ContainerBuilder(); container.RegisterAspectCore(); container.Populate(services); container.RegisterType<CustomService>().As<ICustomService>().InstancePerDependency().AsInterfacesProxy(); return new AutofacServiceProvider(container.Build());}