zoukankan      html  css  js  c++  java
  • 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中了。使用它你可以轻松地拥有一个版本系统,要不然你可以自己实现。

  • 相关阅读:
    34. Find First and Last Position of Element in Sorted Array
    42. Trapping Rain Water
    HDU-2952 Counting Sheep (DFS)
    HDU-1518 Square(DFS)
    HDU-1253 胜利大逃亡 (BFS)
    HDU-1026 Ignatius and the Princess I (BFS)
    最小生成树之Prim算法,Kruskal算法
    HDU-1495 非常可乐(BFS)
    strncpy 用法
    字符串函数总结
  • 原文地址:https://www.cnblogs.com/farb/p/ABPFeatures.html
Copyright © 2011-2022 走看看