Autofac multitenant 用法
What is Multitenant?
官方說法:「A multitenant application is an application that you can deploy one time yet allow separate customers, or “tenants,” to view the application as though it was their own.」。也就是依不同的使用者,作不同的回應或邏輯處理,算是個 Strategy pattern。
其實這樣的功能,在沒有 Multitenant 的情況下,也是可以透過 builder 在註冊物件時,用 Keyed 來實作。例如底下範例。
var builder = new ContainerBuilder();
builder.RegisterType<StudentA>().Keyed<IStudent>("A");
而在要 resolve 物件時,只要使用 ResolveKeyed 然後再傳入 service name 就可回傳正確認物件。
var _container = builder.Build();
var b = _container.ResolveKeyed<IStudent>("A");
但個人覺得,用 Keyed 來實作時會有二個問題。
一、如果範圍很大的話,這樣 Keyed 到底要用到多少次?
二、如果本來就需要用到 Keyed 功能,如果再加上多實作 multitenant,只會更加混混亂。
所以,multitenant 在處理較依不同情境作不同回應及邏輯處理時就相對實用。
舉例來說,有個學校內部的系統發佈後,依不同群組的學生,在整個站台內執行不同的金額計算。底下即說明如何實作。
在實作前,nuget 除了要裝 autofac 外,還要再加裝 multitenant 套件。
using Autofac;
using Autofac.Multitenant;
自己要新建一個實作「ITenantIdentificationStrategy」介面的類別。
介面內的「TryIdentifyTenant」邏輯要如何實作,就看各種情況了。但簡單點的話,其實就只是記個 id 而已。
class TenantIdentificationStrategy : ITenantIdentificationStrategy
{
public object CurrentTenantId { get; set; }
public bool TryIdentifyTenant(out object tenantId)
{
if (this.CurrentTenantId.ToString() == "0")
{
// 0 is the "default tenant ID"
tenantId = null;
}
else
{
// If the current tenant isn't default, return the actual ID.
tenantId = this.CurrentTenantId;
}
return true;
}
}
再來是實作 multitenant 的內容。
var builder = new ContainerBuilder();
builder.RegisterType<StudentA>().As<IStudent>();
var _container = builder.Build();
var _multitenant = new TenantIdentificationStrategy() { CurrentTenantId = "A" };
var multi = new MultitenantContainer(_multitenant, _container);
multi.ConfigureTenant("A", x =>
{
x.RegisterType<TaxA>().As<ITax>();
});
multi.ConfigureTenant("B", x =>
{
x.RegisterType<TaxB>().As<ITax>();
//即使外面的 builder 有註冊,在 tenant 仍可覆寫註冊
x.RegisterType<StudentB>().As<IStudent>();
});
Console.WriteLine(multi.Resolve<ITax>().Calculate());
Console.WriteLine(multi.Resolve<IStudent>().GetName());
_multitenant.CurrentTenantId = "B";
Console.WriteLine(multi.Resolve<ITax>().Calculate());
Console.WriteLine(multi.Resolve<IStudent>().GetName());
首先,先 new 一個剛才實作的類別,然後再當成參數用來 new MultitenantContainer 這個類別。
var _multitenant = new TenantIdentificationStrategy() { CurrentTenantId = "A" };
var multi = new MultitenantContainer(_multitenant, _container);
再來就是設定 tenant,主要參數就是給一個 id,然後在這個 id 內要為各個 interface 註冊哪些物件。
以下面程式來說,我建了「A」跟「B」二種 tenant,意思也就是之後可用不同的 id 來作切換,「multi」這個物件就會依對應到的 tenant 來 resolve 不同的物件。
multi.ConfigureTenant("A", x =>
{
x.RegisterType<TaxA>().As<ITax>();
});
multi.ConfigureTenant("B", x =>
{
x.RegisterType<TaxB>().As<ITax>();
//即使外面的 builder 有註冊,在 tenant 仍可覆寫註冊
x.RegisterType<StudentB>().As<IStudent>();
});
在程式中,可隨時切換不同的 id,用以切換使用不同的 tenant。
_multitenant.CurrentTenantId = "B";
Console.WriteLine(multi.Resolve<IStudent>().GetName());
如果是要 resolve 相同的物件,不會因為不同的 tenant 而有區分的話,則可以在 builder 內即註冊,這樣只要在 tenant 沒有被覆寫,所 resolve 出來的物件都會一樣。
var builder = new ContainerBuilder();
builder.RegisterType<StudentA>().As<IStudent>();
心得:
個人覺得能用在站台有因為不同群組的使用者,而需執行不同的邏輯。因為一整個站台的功能很多,如果能透過這樣切換 tenant,這樣只要在初始的 builder 都設定好,各個功能就能一次切換了。
參考連結: