ABP理論學習之功能管理


返回總目錄


本篇目錄

介紹###

大多數的Saas(多租戶)應用都有不同 功能版本(包)。因此,他們可以給租戶(客戶)提供不同的 價格和功能選項

ABP提供了功能系統使得這個更簡單。我們可以 定義功能,然后檢查某個功能是否對一個租戶 開啟了,最后將功能系統 集成到其他的ABP概念中(如權限和菜單)。

關於IFeatureValueStore

功能系統使用了IFeatureValueStore來獲得功能的值。雖然你可以用自己的方式實現該接口,但是它已經完全實現在了 module-zero項目中。如果沒有實現該接口,那么默認會使用NullFeatureValueStore對所有的功能返回null(此時使用默認的功能值)。

功能類型###

有兩種基本功能類型。

布爾功能

可以是"true"或"false"。這種類型的功能(對於一個版本或者一個租戶)可能是開啟的或者 關閉的

值功能

可以是任意值。雖然它是以字符串存儲和檢索的,但是數值也可以輕松地存儲為字符串。

比如,我們的應用程序可能是一個任務管理應用,我們可能在一個月內對於創建任務會有限制。假如說我們有兩個不同的版本:一個版本每個月允許創建1000個任務,但是另一個每個月允許創建5000個任務。因此,這個功能應該存儲為值,而不是簡單的true或false。

定義功能###

在檢查功能之前應該先定義功能。一個模塊可以通過從FeatureProvider類派生來定義自己的功能。這里有一個定義了3個功能的非常簡單的功能提供者:

public class AppFeatureProvider : FeatureProvider
{
    public override void SetFeatures(IFeatureDefinitionContext context)
    {
        var sampleBooleanFeature = context.Create("SampleBooleanFeature", defaultValue: "false");
        sampleBooleanFeature.CreateChildFeature("SampleNumericFeature", defaultValue: "10");
        context.Create("SampleSelectionFeature", defaultValue: "B");
    }
}

創建功能提供者之后,我們應該在模塊的PreInitialize方法中注冊,如下所示:

Configuration.Features.Providers.Add<AppFeatureProvider>();

基本功能屬性

一個功能的定義至少要求兩個屬性:

  • Name:識別該功能唯一的名字(字符串)。
  • DefaultValue:默認值。當我們需要該功能的值時會用到該屬性,而且對於當前的租戶不可用。

上面的代碼樣例中,我們定義了一個名為"SampleBooleanFeature"布爾功能,它的默認值是"false"(不可用)。我們也定義了兩個值功能(SampleNumericFeature定義為SampleBooleanFeature的孩子)。

提示:為功能名稱創建一個常量字符串,然后在任何地方使用時會防止拼寫失誤。

其他功能屬性

雖然對於ABP來說一個唯一的名稱和默認值屬性已經足夠了,但是對於細節的控制還有許多其他的功能屬性。

  • Scope:FeatureScope枚舉值之一。它可以是Edition(如果只為版本級別設置該功能), Tenant(如果只為租戶級別設置該功能),或者 All(如果為版本和租戶都可以設置該功能,這種情況下,租戶的設置會覆蓋版本的設置)。默認值是All。
  • DisplayName:給用戶顯示該功能名稱的本地化字符串。
  • Description:給用戶顯示該功能細節描述的本地化字符串。
  • InputType:該功能的UI輸入類型。
  • Attributes:任意的自定義鍵值對字典,可以和該功能關聯起來。

讓我們看一下該功能的細節定義:

public class AppFeatureProvider : FeatureProvider
{
    public override void SetFeatures(IFeatureDefinitionContext context)
    {
        var sampleBooleanFeature = context.Create(
            AppFeatures.SampleBooleanFeature,
            defaultValue: "false",
            displayName: L("Sample boolean feature"),
            inputType: new CheckboxInputType()
            );

        sampleBooleanFeature.CreateChildFeature(
            AppFeatures.SampleNumericFeature,
            defaultValue: "10",
            displayName: L("Sample numeric feature"),
            inputType: new SingleLineStringInputType(new NumericValueValidator(1, 1000000))
            );

        context.Create(
            AppFeatures.SampleSelectionFeature,
            defaultValue: "B",
            displayName: L("Sample selection feature"),
            inputType: new ComboboxInputType(
                new StaticLocalizableComboboxItemSource(
                    new LocalizableComboboxItem("A", L("Selection A")),
                    new LocalizableComboboxItem("B", L("Selection B")),
                    new LocalizableComboboxItem("C", L("Selection C"))
                    )
                )
            );
    }

    private static ILocalizableString L(string name)
    {
        return new LocalizableString(name, AbpZeroTemplateConsts.LocalizationSourceName);
    }
}

注意:ABP沒有使用這里的InputType。當為功能創建輸入時,應用程序會使用它們。ABP只是提供了這些選項使得它更容易。

功能層次

正如樣例功能提供者所示,一個功能可以有子功能。一個父母功能一般定義為 布爾功能。只有父母功能可用時,孩子功能才可用。ABP不強制這樣做,但是建議這樣做。

檢查功能###

使用RequireFeature特性

我們可以為方法或類使用RequiredFeature特性,如下所示:

[RequiresFeature("ExportToExcel")]
public async Task<FileDto> GetReportToExcel(...)
{
    ...
}

該方法只有在"ExportToExcel"功能對當前租戶開啟時才會執行(當前租戶從IAbpSession中獲得)。如果沒有開啟該功能,那么就會自動拋出 AbpAuthorizationException

當然,RequiresFeature特性應該用於布爾類型功能。否則,你會得到異常。

RequiresFeature特性注意點
ABP對於功能檢查使用了強大的動態方法攔截(interception)。因此,為方法使用RequiresFeature特性時有一些限制條件:

  • 不能用於私有方法。
  • 不能用於靜態方法。
  • 不能用於非注入類的方法(我們必須使用DI)。

此外,

  • 如果該方法是通過一個接口(如應用服務通過接口調用)調用的,那么我們可以將它用於任何 public的方法。
  • 如果一個方法直接從類的引用調用(如MVC或Web API控制器),那么它應該是virtual的。
  • 如果一個方法是protected,那么該方法應該是 virtual

使用IFeatureChecker

我們可以注入並使用IFeatureChecker來手動檢查一個功能(對於應用服務,MVC和Web API控制器,它會自動注入而且直接可以使用)。

IsEnabled

用於簡單地檢查給定的功能是否開啟。例子:

public async Task<FileDto> GetReportToExcel(...)
{
    if (await FeatureChecker.IsEnabledAsync("ExportToExcel"))
    {
        throw new AbpAuthorizationException("You don't have this feature: ExportToExcel");
    }
    
    ...
}

IsEnabledAsync和其他方法都有同步版本。

當然,IsEnabled方法應該用於布爾類型功能。否則可能會拋異常。

如果你只想檢查一個功能,然后拋出例子中的異常,那么你只需要使用CheckEnabled方法就行了。

GetValue

用於獲得值類型功能的當前值,例子:

var createdTaskCountInThisMonth = GetCreatedTaskCountInThisMonth();
if (createdTaskCountInThisMonth >= FeatureChecker.GetValue("MaxTaskCreationLimitPerMonth").To<int>())
{
    throw new AbpAuthorizationException("You exceed task creation limit for this month, sorry :(");
}

FeatureChecker方法也有對於特定租戶的重載,不僅僅只對於當前的租戶。

客戶端

在客戶端,我們使用abp.features命名空間來獲得該功能的當前值。

isEnabled

var isEnabled = abp.features.isEnabled('SampleBooleanFeature');

getValue

var value = abp.features.getValue('SampleNumericFeature');

功能管理者###

如果需要定義功能,可以注入並使用IFeatureManager

版本說明###

ABP沒有內置的版本系統,因為這么個系統要求數據庫(存儲版本,版本功能,租戶-版本映射等等)。因此,版本系統實現在了module-zero中了。使用它你可以輕松地擁有一個版本系統,要不然你可以自己實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM