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).

설치로 이동

소스 정보

저장소
abpframework/abp-skills
최근 소스 활동
2026년 7월 20일 03:24
감지된 SKILL.md 언어
영어
스타
18
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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.
GitHub에서 보기