Autofac + Interceptors(AOP) 動態代理

  • 2921
  • 0
  • c#
  • 2019-09-28

Autofac 是個IOC容器  不懂IOC 參考 我之前寫 (IOC(控制反轉) , DI(依賴注入) 深入淺出~~)

小弟之前有一個開源框架 AwesomeProxy.Net 裡面簡單介紹 AOP核心概念和如何實作!

Autofac 有寫一個 Autofac.Extras.DynamicProxy 把AOP和IOC容器融合的框架

 

在Autofac使用AOP 需要實現下面幾個步驟

本次範例我們從資料庫中撈取時間資料出來,並使用Thread.Sleep(5000) 作出延遲,判斷時間是否前後一致。

 

第一步(定義攔截器):

我們撰寫一個快取的攔截器繼承IInterceptor 介面,並實現Intercep方法

其中 IInvocation 參數有許多有用的資料

  • Arguments:傳入方法中的參數
  • InvocationTarget :被代理物件
  • MethodInvocationTarget:被代理物件的呼叫方法資訊
  • Proxy:代理物件
  • Method:代理的呼叫方法資訊
  • ReturnValue:呼叫方法的回傳值

這幾個欄位是我們比較常用的資訊

public class TimeInterceptor : IInterceptor
{
	private ITimeService _timeService;
	public TimeInterceptor(ITimeService s)
	{
		
		_timeService = s;
	}

	public void Intercept(IInvocation invocation)
	{
		var time = CallContext.GetData("time")?.ToString();
		if (time == null)
		{
			//如果沒有快取 執行呼叫Service
			invocation.Proceed();
			CallContext.SetData("time", invocation.ReturnValue);
		}
		else
		{
			//如果有快取直接取值
			invocation.ReturnValue = time;
		}
	}
}

他使用到 TimeService 模擬從資料庫中撈取資料出來

public interface ITimeService
{
	string GetTime();
}

public class TimeService : ITimeService
{
	public string GetTime()
	{
		return DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
	}
}

第二步(標記攔截器): 

使用Intercept標籤並帶入要攔截類別型態.

[Intercept(typeof(TimeInterceptor))]
public class Person : IPerson
{
    public string SaySomething()
    {
        return DateTime.Now.ToLongTimeString();
    }
}

public interface IPerson
{
    string SaySomething();
}

第三步(註冊攔截器到容器中): 

這邊有兩個小細節

  1. 如果是註冊介面使用EnableInterfaceInterceptors,註冊一般類別使用EnableClassInterceptors
  2. 註冊攔截器入容器

因為這個範例使用.As<IPerson>()所以我們要呼叫EnableInterfaceInterceptors

var builder = new ContainerBuilder();

builder.RegisterType<TimeInterceptor>(); //註冊攔截器

builder.RegisterType<Person>()
		.As<IPerson>()
		.EnableInterfaceInterceptors();

//註冊時間Service
builder.RegisterType<TimeService>().As<ITimeService>();

return builder.Build();

 


終極密技

我們可以簡化註冊的寫法使用RegisterAssemblyTypes取所有的IInterceptor並註冊到Autofac中

//將Assembly所有實現IInterceptor註冊入IOC容器中
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
	.AssignableTo(typeof(IInterceptor));

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
	.AsImplementedInterfaces()
	.EnableInterfaceInterceptors();

最後在使用下面註冊程式碼將所有用Interceptors掛載到相對應的類別上

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
	.AsImplementedInterfaces()
	.EnableInterfaceInterceptors();

UserServicePerson類別都可以使用到下面兩個攔截器

  • CacheInterceptor
  • LogInterceptor
[Intercept(typeof(CacheInterceptor))]
[Intercept(typeof(LogInterceptor))]
public class UserService:IUserService
{
	public virtual void ModifyUserInfo(UserModel model)
	{
		Console.WriteLine("UserService Value");
	}
}

[Intercept(typeof(CacheInterceptor))]
[Intercept(typeof(LogInterceptor))]
public class Person : IPerson
{
	public string SaySomething()
	{
		return DateTime.Now.ToLongTimeString();
	}
}

最後結果如下圖.

 

原始碼連結

小結

Autofac + DynamicProxy 有一個很大優勢,是可以把要注入的抽象動作一起注入攔截器中

例如本次範例我們將ITimeService 使用建構子注入法,注入至TimeInterceptor 攔截器中

讓系統和寫法擁有更多更多的彈性


如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^