Service怎麼做到像MiddleWare一樣的東西
在網頁程式中可以透過MiddleWare來做到關注點分離,但是那是在請求中可以去攔截跟處理請求,如果今天腳色換成Service之類的程式該如何處理呢,這就是我們今天要介紹的內容AoP。
AoP
「天啊~~~我要怎麼處理每個方法的輸入輸出的log阿。」
一進辦公室小光就聽到大頭的慘叫聲,因為大頭昨天在追一個bug追到沒有方向,所以今天一大早大頭決定要先把每個方法加上輸入跟輸出的log。
「你知道甚麼是AoP嗎」
聽到大頭的慘叫後老K這麼問題,這時大頭像找到救星一般臉上露出了曙光。
「前輩,救救我吧。」
所以今天就跟著小光還有大頭了解一下甚麼是AoP以及在dotnetcore做到AoP。
甚麼是AoP
AoP就是剖面導向程式設計,白話文就是如同關注點分離,對於開發的程式關注於本身的邏輯開發,而其他與本身邏輯無關的功能就從程式中抽離,然後在像切蛋糕一樣切開來把額外的功能加到程式內,就如同一開始的大頭在本身的程式內沒有日誌的功能,所以現在我們在攔截原來的程式並在這裡加入日誌的功能。不過說那麼多在dotnetcore內該如何處理呢,接下來我們介紹DynamicProxy這個套件。
DynamicProxy
所以我們在dotnetcore中實作AoP就是透過dynamicproxy這個套件,所以接下來我們說明如何透過autofac及dynamicproxy做到AoP。
環境處理
在介紹如何撰寫程式做到AoP之前先說明我們需要使用到哪些套件,接下來我們先安裝以下兩個套件即可。
dotnet add package Autofac.Extras.DynamicProxy
dotnet add package Autofac.Extensions.DependencyInjection
實作攔截者
在安裝完套件之後我們開始來開發AoP,首先假設我們的需求是開發一個寄送email的服務,但是我們需要輸入及輸出的參數,所以我們先定義介面以及實作。
public interface IEmailService
{
string Send(string message);
}
[Intercept(typeof(CallLogger))]
public class EmailService : IEmailService
{
public string Send(string message)
{
return message;
}
}
在實作時我們透過Intercept
這個屬性來注入攔截者,接下來說明攔截者怎麼實作。
public class CallLogger : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"You are invoke {invocation.Method.Name}, args:{JsonSerializer.Serialize(invocation.Arguments)}");
invocation.Proceed();
Console.WriteLine($"You are finished {invocation.Method.Name}, result:{JsonSerializer.Serialize(invocation.ReturnValue)}");
}
}
主要攔截者就是繼承IInterceptor
並實作public void Intercept(IInvocation invocation)
這個方法,這邊注意invocation.Proceed();
就是執行被攔截的方法,所以我們把需要做的需求寫在那方法之前跟之後即可。最後來說明如何註冊攔截者跟本來的程式。
builder.Register(c => new CallLogger());
builder.RegisterType<EmailService>().As<IEmailService>().InstancePerDependency().EnableInterfaceInterceptors();
插入攔截器的方法有下列兩種。
EnableClassInterceptors | 產生一個類別繼承要攔截的類別,所以方法要加virtual才能攔截 |
EnableInterfaceInterceptors | 動態產生一個代理接口 |
除了透過Attribute之外,還可以在注入時加上,所以注入攔截器的方法有下列兩種。
加Attribute | [Intercept(typeof(CallLogger))] |
注入攔截器 | builder.RegisterType |
後記
透過AoP可以把一般物件中做到關注點分離,如此不只在處理請求中,甚至於在處理一般的函式都可以專注在該函式主要處理的邏輯上,而其它非需要關注的邏輯就抽到攔截者來處理。