ABP框架使用攔截器動態配置租戶過濾器

文章目錄

前言

最近項目要求在ABP框架中根據TenantId是否爲空來配置是否禁用租戶過濾器。ABP自身給我我們禁用租戶過濾器的兩種方法官方文檔

  • 方法一:使用工作單元
using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
    var people2 = _personRepository.GetAllList();                
}
  • 方法二:全局禁用過濾器
Configuration.UnitOfWork.OverrideFilter(AbpDataFilters.MayHaveTenant, false);

但是方法一要修改的地方很多,嫌麻煩;方法二隻能全局在Configuration中配置,不能動態改變,也不合適。於是我查閱了APB AOP和攔截技術,另外查閱了ABP自身註冊了攔截器——UnitOfWorkRegistrar,會默認爲繼承自IRepository或者是IApplicationService的兩種類型添加UnitOfWork特性,於是便可以通過攔截方法去實現動態禁用過濾器。

具體實現

首先在Application 層新建一個TenantInterceptor 繼承IInterceptor接口

public class TenantInterceptor : IInterceptor
{
    public ILogger Logger { get; set; }

    public TenantInterceptor()
    {
        Logger = NullLogger.Instance;
    }

    public void Intercept(IInvocation invocation)
    {
        // 從invocation中拿到當前註冊進來的工作單元,主要用於獲取TenantId
        Type t = invocation.InvocationTarget.GetType();
        var unitOfWorkManager = (t.GetProperty("UnitOfWorkManager").GetValue(invocation.InvocationTarget)) as IUnitOfWorkManager;
        //根據TenantId是否禁用租戶過濾器
        if (unitOfWorkManager.Current.GetTenantId().HasValue)
        {
            invocation.Proceed(); // 執行方法體
        }
        else {
        	// 禁用租戶
        	// PS:這裏不可以使用 using      	
            unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant, AbpDataFilters.MustHaveTenant);
            invocation.Proceed(); // 執行方法體
        }
    }
}

攔截器裏的內容很簡單,主要就是根據工作單元獲取TenantId來動態禁用過濾器。因爲這裏沒有需要返回的東西,也就不用分同步異步去攔截。
接下來就是爲所需要禁用租戶過濾器的類註冊攔截器

public static class TenantInterceptorRegistrar
{
    public static void Initialize(IKernel kernel)
    {

        kernel.ComponentRegistered += Kernel_ComponentRegistered;
    }

    private static void Kernel_ComponentRegistered(string key, IHandler handler)
    {
        var implementationType = handler.ComponentModel.Implementation.GetTypeInfo();
        // 爲實現了接口IRepository接口的所有類註冊攔截器
        //if (typeof(IRepository).IsAssignableFrom(implementationType))
        //{
        //    handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(TenantInterceptor)));
        //}
		
		// 爲指定類註冊攔截器
        if (InternalAsyncHelper.DisableFilterTenantTypes.Any(a => a.IsAssignableFrom(implementationType)))
        {
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(TenantInterceptor)));
        }
    }
}

internal static class InternalAsyncHelper
{
    public static Type[] DisableFilterTenantTypes =
    {
        typeof(IRepository<Student,Guid>),
        typeof(IRepository<School,Guid>)
    };
}

TenantInterceptorRegistrarInitialize方法中,首先會注入整個ABP系統中唯一的IIocManager,然後就是訂閱唯一的IocContainer這個容器的ComponentRegistered事件,在訂閱事件中首先是獲取當前觸發此事件的類型信息,然後根據需求註冊TenantInterceptor這個攔截器。

這裏有一點需要注意,本來想爲實現了IApplicationService接口的類註冊攔截器,但是ASP.NET Boilerplate使用動態方法攔截的功能有一些限制

  • 如果通過接口調用該方法,則可以將其用於任何公共方法(例如,通過接口使用的Application Services)。
  • 如果直接從類引用(例如ASP.NET MVC或Web API控制器)中調用方法,則該方法應爲虛擬方法。
  • 一種方法應該是虛擬的,如果它的保護。

也就是如果將服務作爲客戶端的Web API控制器公開,那麼方法必須是虛方法(virtual) 附上官方Git issues

最後一步就是把攔截器在模塊文件中初始化

public class ApplicationCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        TenantInterceptorRegistrar.Initialize(IocManager.IocContainer.Kernel);
    }

    public override void Initialize()
    {
    }
}

這樣就可以按着自己的需要在DisableFilterTenantTypes 中配置自己想配置的倉儲了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章