Castle 是 2003 年誕生于 Apache Avalon 專案,目的是為了創建一個IOC 框架,發展到現在已經有四個組件:
- ORM組件:ActiveRecord
- IOC組件:Windsor
- 動態代理組件:DynamicProxy
- Web MVC組件:MonoRail
本文主要介紹 動態代理組件 Castle.DynamicProxy
基本用法
Castle.DynamicProxy 是通過 Emit 反射動態生成代理類來實作的,效率相對靜態植入要慢一點,但比普通的反射又高一些,動態代理只對公共介面方法、類中的虛方法生效,因為只有介面中的方法、類中的虛方法才可以在子類中重寫,
基于介面的攔截器
public interface IProductRepository
{
void Add(string name);
}
public class ProductRepository : IProductRepository
{
public void Add(string name) => Console.WriteLine($"新增產品:{name}");
}
public class LoggerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 執行前");
//呼叫業務方法
invocation.Proceed();
Console.WriteLine($"{methodName} 執行完畢");
}
}
class Program
{
static void Main(string[] args)
{
ProxyGenerator generator = new ProxyGenerator();
IInterceptor loggerIntercept = new LoggerInterceptor();
IProductRepository productRepo = new ProductRepository();
IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
proxy.Add("大米");
Console.Read();
}
}
基于類的攔截器
public class ProductRepository
{
public virtual void Add(string name) => Console.WriteLine($"新增產品:{name}");
}
static void Main(string[] args)
{
ProxyGenerator generator = new ProxyGenerator();
IInterceptor loggerIntercept = new LoggerInterceptor();
ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept);
// 使用 CreateClassProxy 泛型方法可以省去實體化代碼
//ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);
proxy.Add("大米");
}
在上例中,如果 ProductRepository.Add 不是虛方法,也不會報錯,但是攔截器不會被呼叫,
異步函式攔截
Castle.DynamicProxy 對異步函式的攔截跟同步沒啥差別,只是,如果要在方法執行完成后插入內容,需要 await
public class ProductRepository
{
public virtual Task Add(string name)
{
return Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"異步新增產品:{name}");
});
}
}
public class LoggerInterceptor : IInterceptor
{
public async void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 執行前");
invocation.Proceed();
// 不 await 的話將會先輸出“執行完畢”,再輸出“異步新增產品”
var task = (Task)invocation.ReturnValue;
await task;
Console.WriteLine($"{methodName} 執行完畢");
}
}
上面這個寫法是簡單粗暴的,如果碰到回傳值是 Task<TResult>,或者不是異步函式,就會出錯,所以這里是要對回傳值進行一個判斷的,
可以使用 Castle.Core.AsyncInterceptor 包,它包裝了 Castle,使異步呼叫更簡單,
Castle.Core.AsyncInterceptor 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor
public class ProductRepository : IProductRepository
{
public Task Add(string name)
{
return Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"異步新增產品:{name}");
});
}
public Task<string> Get()
{
return Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"獲取產品");
return "大米";
});
}
}
public class LoggerInterceptor : IAsyncInterceptor
{
public void InterceptAsynchronous(IInvocation invocation)
{
invocation.ReturnValue = https://www.cnblogs.com/gl1573/p/InternalInterceptAsynchronous(invocation);
}
async Task InternalInterceptAsynchronous(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 異步執行前");
invocation.Proceed();
await (Task)invocation.ReturnValue;
Console.WriteLine($"{methodName} 異步執行完畢");
}
public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = https://www.cnblogs.com/gl1573/p/InternalInterceptAsynchronous(invocation);
Console.WriteLine(((Task)invocation.ReturnValue).Id);
}
private async Task InternalInterceptAsynchronous(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 異步執行前");
invocation.Proceed();
var task = (Task<TResult>)invocation.ReturnValue;
TResult result = await task;
Console.WriteLine(task.Id);
Console.WriteLine($"{methodName} 異步執行完畢");
return result;
}
public void InterceptSynchronous(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 同步執行前");
invocation.Proceed();
Console.WriteLine($"{methodName} 同步執行完畢");
}
}
class Program
{
static void Main(string[] args)
{
ProxyGenerator generator = new ProxyGenerator();
IAsyncInterceptor loggerIntercept = new LoggerInterceptor();
IProductRepository productRepo = new ProductRepository();
IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
proxy.Get();
}
}
這是 Castle.Core.AsyncInterceptor 提供的示例寫法,這里有個問題,也是我的疑惑,invocation.ReturnValue = https://www.cnblogs.com/gl1573/p/InternalInterceptAsynchronous(invocation); 將導致代理回傳的 Task 是一個新的 Task,這一點我們可以輸出 Task.Id 來確認,個人感覺有點畫蛇添足,
public async void InterceptAsynchronous<TResult>(IInvocation invocation)
{
var methodName = invocation.Method.Name;
Console.WriteLine($"{methodName} 異步執行前");
invocation.Proceed();
var task = (Task<TResult>)invocation.ReturnValue;
await task;
Console.WriteLine($"{methodName} 異步執行完畢");
}
這樣就挺好的,
如果有小伙伴知道為什么要回傳一個新的 Task,請留言告訴我,謝謝!
Autofac 集成
Autofac.Extras.DynamicProxy 是一個 Autofac 擴展,可與 Castle 一起提供 AOP 攔截,
基于介面的攔截器
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注冊攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
//注冊要攔截的服務
builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
.EnableInterfaceInterceptors() //啟用介面攔截
.InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器
IContainer container = builder.Build();
IProductRepository productRepo = container.Resolve<IProductRepository>();
productRepo.Add("大米");
}
基于類的攔截器
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注冊攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
//注冊要攔截的服務
builder.RegisterType<ProductRepository>()
.EnableClassInterceptors() //啟用類攔截
.InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器
IContainer container = builder.Build();
ProductRepository productRepo = container.Resolve<ProductRepository>();
productRepo.Add("大米");
}
異步函式攔截
Castle.Core.AsyncInterceptor 中,IAsyncInterceptor 介面并不集成 IInterceptor 介面,而 Autofac.Extras.DynamicProxy 是系結 Castle 的,所以按上面同步攔截的寫法是會報錯的,
IAsyncInterceptor 提供了 ToInterceptor() 擴展方法來進行型別轉換,
public class LoggerInterceptor : IInterceptor
{
readonly LoggerAsyncInterceptor interceptor;
public LoggerInterceptor(LoggerAsyncInterceptor interceptor)
{
this.interceptor = interceptor;
}
public void Intercept(IInvocation invocation)
{
this.interceptor.ToInterceptor().Intercept(invocation);
}
}
public class LoggerAsyncInterceptor : IAsyncInterceptor
{
public void InterceptAsynchronous(IInvocation invocation)
{
//...
}
public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
//...
}
public void InterceptSynchronous(IInvocation invocation)
{
//...
}
}
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
//注冊攔截器
builder.RegisterType<LoggerInterceptor>().AsSelf();
builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();
//注冊要攔截的服務
builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
.EnableInterfaceInterceptors() //啟用介面攔截
.InterceptedBy(typeof(LoggerInterceptor)); //指定攔截器
var container = builder.Build();
IProductRepository productRepo = container.Resolve<IProductRepository>();
productRepo.Get();
}
參考
https://www.cnblogs.com/youring2/p/10962573.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/262315.html
標籤:C#
上一篇:C# 介面(Interface)
