本文共 4972 字,大约阅读时间需要 16 分钟。
大多数的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();
一个功能的定义至少要求两个属性:
上面的代码样例中,我们定义了一个名为"SampleBooleanFeature"布尔功能,它的默认值是"false"(不可用)。我们也定义了两个值功能(SampleNumericFeature定义为SampleBooleanFeature的孩子)。
提示:为功能名称创建一个常量字符串,然后在任何地方使用时会防止拼写失误。
虽然对于ABP来说一个唯一的名称和默认值属性已经足够了,但是对于细节的控制还有许多其他的功能属性。
让我们看一下该功能的细节定义:
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不强制这样做,但是建议这样做。
我们可以为方法或类使用RequiredFeature特性,如下所示:
[RequiresFeature("ExportToExcel")]public async TaskGetReportToExcel(...){ ...}
该方法只有在"ExportToExcel"功能对当前租户开启时才会执行(当前租户从IAbpSession中获得)。如果没有开启该功能,那么就会自动抛出 AbpAuthorizationException。
当然,RequiresFeature特性应该用于布尔类型功能。否则,你会得到异常。
RequiresFeature特性注意点
ABP对于功能检查使用了强大的动态方法拦截(interception)。因此,为方法使用RequiresFeature特性时有一些限制条件:此外,
我们可以注入并使用IFeatureChecker来手动检查一个功能(对于应用服务,MVC和Web API控制器,它会自动注入而且直接可以使用)。
IsEnabled
用于简单地检查给定的功能是否开启。例子:
public async TaskGetReportToExcel(...){ 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 ()){ 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中了。使用它你可以轻松地拥有一个版本系统,要不然你可以自己实现。
本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/ABPFeatures.html,如需转载请自行联系原作者