Skip to main content

manage-settings-and-features

ABP settings (config values) and features (tenant/edition on/off or valued capabilities). USE FOR: SettingDefinitionProvider / ISettingProvider, FeatureDefinitionProvider / IFeatureChecker / [RequiresFeature], custom value providers, ISettingManager and IFeatureManager. DO NOT USE FOR: typed distributed caching or locking (distributed-caching-and-locking); user/role authorization (permissions-and-authorization); multi-tenant resolution (configure-multi-tenancy).

Aller à l'installation

Informations de source

Dépôt
abpframework/abp-skills
Dernière activité de la source
20 juillet 2026 à 03:24
Langue détectée de SKILL.md
anglais
Étoiles
18
Forks
0

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
manage-settings-and-features
description
ABP settings (config values) and features (tenant/edition on/off or valued capabilities). USE FOR: SettingDefinitionProvider / ISettingProvider, FeatureDefinitionProvider / IFeatureChecker / [RequiresFeature], custom value providers, ISettingManager and IFeatureManager. DO NOT USE FOR: typed distributed caching or locking (distributed-caching-and-locking); user/role authorization (permissions-and-authorization); multi-tenant resolution (configure-multi-tenancy).
license
MIT
# Managing Settings and Features in ABP Settings and features look similar (both are name/value pairs resolved through a provider fallback chain) but answer different questions: - **Settings** = configuration values for the application/tenant/user (SMTP host, default timezone, page size). Resolved per user > tenant > global > configuration > default. Used everywhere, not tied to multi-tenancy. - **Features** = capabilities you grant to a **tenant** (or edition), usually boolean on/off or a limit value (max product count). Designed for SaaS/multi-tenant: "does this tenant's plan include PDF reporting?". Rule of thumb: if a customer's plan/edition controls it, it's a feature; if it's a tunable configuration value, it's a setting. ## When to Use - Declare tunable configuration values (SMTP host, page size) and read them with provider fallback. - Grant per-tenant or per-edition capabilities (on/off or valued) for a SaaS app. - Gate a service method or class behind a feature declaratively or imperatively. - Change setting or feature values from code, or add a custom value provider. ## When Not to Use - **Typed distributed caching or cross-instance locking** — use the **distributed-caching-and-locking** skill. - **Authorizing users/roles with permissions** — use the **permissions-and-authorization** skill. Features gate a tenant's *plan*, permissions gate a *user's* rights. - **Configuring or resolving multi-tenancy itself** — use the **configure-multi-tenancy** skill. ## Settings ### Defining settings Derive from `SettingDefinitionProvider` (auto-discovered, transient). For a DDD module this usually lives in the `Domain` layer. ```csharp public class EmailSettingProvider : SettingDefinitionProvider { public override void Define(ISettingDefinitionContext context) { context.Add( new SettingDefinition("Smtp.Host", "127.0.0.1"), new SettingDefinition("Smtp.Port", "25"), new SettingDefinition("Smtp.UserName"), new SettingDefinition("Smtp.Password", isEncrypted: true), new SettingDefinition("Smtp.EnableSsl", "false") ); } } ``` `SettingDefinition` constructor parameters (only `name` is mandatory): `name`, `defaultValue = null`, `displayName = null`, `description = null`, `isVisibleToClients = false`, `isInherited = true`, `isEncrypted = false`. - `IsVisibleToClients` defaults to **false** — set it true to expose the value to browser/JS code. - `IsEncrypted` encrypts on save / decrypts on read via `ISettingEncryptionService`. - Define setting names as `const string` instead of magic strings. To tweak a setting defined by a depended module, query and mutate it in your own provider: ```csharp var smtpHost = context.GetOrNull("Abp.Mailing.Smtp.Host"); if (smtpHost != null) { smtpHost.DefaultValue = "mail.mydomain.com"; } ``` ### Reading setting values Inject `ISettingProvider`. Its interface methods are `GetOrNullAsync(name)` → `Task<string?>`, `GetAllAsync(string[] names)` and `GetAllAsync()` → `Task<List<SettingValue>>`; the typed/bool helpers like `IsTrueAsync` and `GetAsync<T>` are extension methods. ```csharp string? userName = await _settingProvider.GetOrNullAsync("Smtp.UserName"); bool enableSsl = await _settingProvider.IsTrueAsync("Smtp.EnableSsl"); int port = await _settingProvider.GetAsync<int>("Smtp.Port"); // defaultValue optional ``` `IApplicationService` already property-injects `ISettingProvider` (use the `SettingProvider` property). ### Value providers and store `ISettingProvider` resolves a value by walking 5 built-in providers; the first that returns non-null wins. Fallback goes bottom → top: `UserSettingValueProvider` (U) > `TenantSettingValueProvider` (T) > `GlobalSettingValueProvider` (G) > `ConfigurationSettingValueProvider` (C) > `DefaultValueSettingValueProvider` (D). - Configuration provider reads the `"Settings"` section of `appsettings.json` (or env / user secrets). - Global/Tenant/User providers read through `ISettingStore`. The core ships `NullSettingStore` (always null); the **Setting Management module** implements a database-backed `ISettingStore` — that's what actually persists changed values. - Custom provider: derive from `SettingValueProvider` (unique `Name`), then register via `Configure<AbpSettingOptions>(o => o.ValueProviders.Add<CustomSettingValueProvider>())`. ## Features ### Defining features Derive from `FeatureDefinitionProvider` (auto-discovered). Usually placed in the `Application.Contracts` project. You add a **group** first, then features under it. ```csharp using Volo.Abp.Features; using Volo.Abp.Validation.StringValues; public class MyFeatureDefinitionProvider : FeatureDefinitionProvider { public override void Define(IFeatureDefinitionContext context) { var myGroup = context.AddGroup("MyApp"); myGroup.AddFeature("MyApp.PdfReporting", defaultValue: "false"); myGroup.AddFeature( "MyApp.MaxProductCount", defaultValue: "10", valueType: new FreeTextStringValueType(new NumericValueValidator(0, 1000000)) ); } } ``` Optional feature properties: `DisplayName`, `Description`, `ValueType` (`ToggleStringValueType` for on/off, `FreeTextStringValueType` for free text, `SelectionStringValueType` for a dropdown), `IsVisibleToClients` (default **true**), `Properties`. Child features (selectable only when the parent is enabled) via `CreateChild(...)` on the returned `FeatureDefinition`. Modify a depended module's feature with `context.GetGroupOrNull("SomeModule")`. ### Checking features Two ways: the declarative `[RequiresFeature]` attribute or the `IFeatureChecker` service. ```csharp public class ReportingAppService : ApplicationService, IReportingAppService { [RequiresFeature("MyApp.PdfReporting")] // throws AbpAuthorizationException if not enabled public async Task<PdfReportResultDto> GetPdfReportAsync() { return await GenerateReportAsync(); } } ``` - `[RequiresFeature]` works on a method or a class. Multiple names check "any enabled"; set `RequiresAll = true` to require all. - It relies on interception: for non-interface services methods must be `virtual` (class-proxy requirement); an `async`/Task-returning method is recommended but not required. Controllers/Razor pages are exempt (filters handle them). `IFeatureChecker` for imperative checks. Interface methods: `IsEnabledAsync(name)`, `IsEnabledAsync(string[] names)`, `GetOrNullAsync(name)` → `Task<string?>`. Typed/check helpers are extensions. ```csharp if (await _featureChecker.IsEnabledAsync("MyApp.PdfReporting")) { /* ... */ } int max = await _featureChecker.GetAsync<int>("MyApp.MaxProductCount"); await _featureChecker.CheckEnabledAsync("MyApp.PdfReporting"); // throws if disabled ``` ### Value providers, management, and configuration Feature values resolve through providers (first non-null wins), in order: `TenantFeatureValueProvider` > `EditionFeatureValueProvider` (edition id from `AbpClaimTypes.EditionId`) > `ConfigurationFeatureValueProvider` (reads the `"Features"` section of `appsettings.json`) > `DefaultValueFeatureValueProvider`. Custom provider: derive from `FeatureValueProvider`, register via `Configure<AbpFeatureOptions>(o => o.ValueProviders.Add<...>())`. Persistence is `IFeatureStore` — the **Feature Management module** implements it (DB-backed) and ships in the startup template. To change feature values from code inject `IFeatureManager`: ```csharp await _featureManager.SetForTenantAsync(tenantId, "MyApp.PdfReporting", true.ToString()); ``` Admins normally set feature values via the feature-management modal on the Tenant Management page. To change **setting** values from code, inject `ISettingManager` (namespace `Volo.Abp.SettingManagement`, from the **Setting Management module** package `Volo.Abp.SettingManagement.Domain`). It persists to the correct provider scope: ```csharp await _settingManager.SetGlobalAsync("MyApp.SmtpHost", "smtp.acme.com"); await _settingManager.SetForTenantAsync(tenantId, "MyApp.SmtpHost", "smtp.tenant.com"); await _settingManager.SetForUserAsync(userId, "MyApp.Theme", "dark"); ``` `ISettingProvider` (read) is in the framework, but writing needs the Setting Management module — a host that only references `Volo.Abp.Settings` can read but not persist changes. ## Validation - Build the app; `SettingDefinitionProvider` and `FeatureDefinitionProvider` are auto-discovered at startup. - Read a setting back through `ISettingProvider` and confirm the fallback resolves the expected layer (e.g. an `appsettings.json` `"Settings"` value overrides the default). - Confirm a `[RequiresFeature]`-guarded method throws `AbpAuthorizationException` when the feature is disabled and runs when enabled. - Confirm persisted values require the Setting Management / Feature Management module (the core `NullSettingStore` always returns null, so without it only configuration/default values resolve). ## Common Pitfalls - **`[RequiresFeature]` silently not enforced** — it relies on interception, so for non-interface services the method must be `virtual` (class-proxy requirement). An `async`/Task-returning method is recommended but not required. Controllers/Razor pages are exempt (filters handle them instead). - **Expecting changed setting/feature values to persist without the management module** — the core ships `NullSettingStore` (always null); DB persistence comes from the **Setting Management** / **Feature Management** modules. - **Confusing features with permissions** — features gate a *tenant/edition's* plan, permissions gate a *user's* rights. Don't use a feature where a permission belongs. - **`IsVisibleToClients` defaults differ** — settings default to **false** (hidden from browser/JS), features default to **true**. Set it explicitly when the default is wrong for your case. - **Using magic strings for names** — define setting/feature names as `const string` to avoid typos that resolve to a missing (null) value.
Voir sur GitHub