diff --git a/docs/en/Cancellation-Token-Provider.md b/docs/en/Cancellation-Token-Provider.md new file mode 100644 index 0000000000..7f7a78f8ad --- /dev/null +++ b/docs/en/Cancellation-Token-Provider.md @@ -0,0 +1,71 @@ +# Cancellation Token Provider + +A `CancellationToken` enables cooperative cancellation between threads, thread pool work items, or `Task` objects. To handle the possible cancellation of the operation, ABP Framework provides `ICancellationTokenProvider` to obtain the `CancellationToken` itself from the source. + +> To get more information about `CancellationToken`, see [Microsoft Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken). + +## ICancellationTokenProvider + +`ICancellationTokenProvider` is an abstraction to provide `CancellationToken` for different scenarios. + +Generally, you should pass the `CancellationToken` as a parameter for your method to use it. With the `ICancellationTokenProvider` you don't need to pass `CancellationToken` for every method. `ICancellationTokenProvider` can be injected with the **dependency injection** and provides the token from it's source. + +**Example:** + +```csharp +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace MyProject +{ + public class MyService : ITransientDependency + { + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public MyService(ICancellationTokenProvider cancellationTokenProvider) + { + _cancellationTokenProvider = cancellationTokenProvider; + } + + public async Task DoItAsync() + { + while (_cancellationTokenProvider.Token.IsCancellationRequested == false) + { + // ... + } + } + } +} +``` + +## Built-in providers + +- `NullCancellationTokenProvider` + + The `NullCancellationTokenProvider` is a built in provider and it supply always `CancellationToken.None`. + +- `HttpContextCancellationTokenProvider` + + The `HttpContextCancellationTokenProvider` is a built in default provider for ABP Web applications. It simply provides a `CancellationToken` that is source of the web request from the `HttpContext`. + +## Implementing the ICancellationTokenProvider + +You can easily create your CancellationTokenProvider by creating a class that implements the `ICancellationTokenProvider` interface, as shown below: + +```csharp +using System.Threading; + +namespace AbpDemo +{ + public class MyCancellationTokenProvider : ICancellationTokenProvider + { + public CancellationToken Token { get; } + + private MyCancellationTokenProvider() + { + + } + } +} +``` diff --git a/docs/en/Global-Features.md b/docs/en/Global-Features.md index a0a593288c..cfbf3f98b7 100644 --- a/docs/en/Global-Features.md +++ b/docs/en/Global-Features.md @@ -1,3 +1,117 @@ # Global Features +The purpose of the Global Feature System is to **add a module to your application but disable the features you don't want to use** (or enable only the ones you need). Notice that the features are not determined on runtime, you must select the features **on development time**. Because it will not create database tables, APIs and other stuff for unused features, which is not possible to change then on the runtime. -TODO (see [#5061](https://github.com/abpframework/abp/issues/5061) until this is documented). \ No newline at end of file +## Installation +> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. + +### Using the ABP CLI + +Open a command line window in the folder of the project (.csproj file) and type the following command: + +```bash +abp add-package Volo.Abp.GlobalFeatures +``` + +## Implementation + +Global Feature system aims module based feature management . A module has to have own Global Features itself. + +### Define a Global Feature + +A feature class is something like that: + +```csharp +[GlobalFeatureName(Name)] +public class PaymentFeature : GlobalFeature +{ + public const string Name = "Shopping.Payment"; + + public PaymentFeature(GlobalModuleFeatures module) : base(module) + { + } +} +``` + +### Define Global Module Features + +All features of a module have to be defined in a Global Module Features class. + +```csharp +public class GlobalShoppingFeatures : GlobalModuleFeatures +{ + public const string ModuleName = "Shopping"; + + public GlobalShoppingFeatures(GlobalFeatureManager featureManager) : base(featureManager) + { + AddFeature(new PaymentFeature(this)); + // And more features... + } +} +``` + +## Usage + +### Enable/Disable Features + +Global features are managed by modules. Module Features have to be added to Modules of GlobalFeatureManager. + +```csharp +// GerOrAdd might be useful to be sure module features are added. +var shoppingGlobalFeatures = GlobalFeatureManager.Instance.Modules + .GetOrAdd( + GlobalShoppingFeatures.ModuleName, + ()=> new GlobalShoppingFeatures(GlobalFeatureManager.Instance)); + +// Able to Enable/Disable with generic type parameter. +shoppingGlobalFeatures.Enable(); +shoppingGlobalFeatures.Disable(); + +// Also able to Enable/Disable with string feature name. +shoppingGlobalFeatures.Enable(PaymentFeature.Name); +shoppingGlobalFeatures.Disable("Shopping.Payment"); +``` + +### Check if a feature is enabled + +```csharp +GlobalFeatureManager.Instance.IsEnabled() +GlobalFeatureManager.Instance.IsEnabled("Shopping.Payment") +``` + +Both methods return `bool`. + +```csharp +if (GlobalFeatureManager.Instance.IsEnabled()) +{ + // Some strong payment codes here... +} +``` + +Beside the manual check, there is `[RequiresGlobalFeature]` attribute to check it declaratively for a controller or page. ABP returns 404 if the related feature was disabled. + +```csharp +[RequiresGlobalFeature(typeof(CommentsFeature))] +public class PaymentController : AbpController +{ + // ... +} +``` + +## When to configure Global Features? +Global Features have to be configured before application startup. So best place to configuring it is `PreConfigureServices` with **OneTimeRunner** to make sure it runs one time. + +```csharp +private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + OneTimeRunner.Run(() => + { + GlobalFeatureManager.Instance.Modules.Foo().EnableAll(); + }); +} +``` + +## Features vs Global Features +[Features](Features.md) & [Global Features](Global-Features.md) are totally different systems. + +Features are used to switch on/off application feature for each tenant. So Features, only hides disabled ones, but with Global Features, disabled features pretends like never existed in application. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 5e0f0b469a..4d2b2419c2 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -343,6 +343,10 @@ { "text": "Virtual File System", "path": "Virtual-File-System.md" + }, + { + "text": "Cancellation Token Provider", + "path": "Cancellation-Token-Provider.md" } ] }, diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs index a81632f8d5..c8f68a1ab0 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureDictionary.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Volo.Abp.GlobalFeatures { diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs index 8ba07554dc..e0662857e1 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureManager.cs @@ -1,5 +1,5 @@ using System; - using System.Collections.Generic; +using System.Collections.Generic; using JetBrains.Annotations; namespace Volo.Abp.GlobalFeatures @@ -26,7 +26,6 @@ namespace Volo.Abp.GlobalFeatures } public virtual bool IsEnabled() - where TFeature : GlobalFeature { return IsEnabled(GlobalFeatureNameAttribute.GetName()); } diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs index 2b987f6cb1..96905b43bf 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeatureNameAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using JetBrains.Annotations; @@ -17,7 +17,6 @@ namespace Volo.Abp.GlobalFeatures } public static string GetName() - where TFeature : GlobalFeature { return GetName(typeof(TFeature)); } diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs index 7d1b844d45..9a8afd236d 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalModuleFeatures.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using JetBrains.Annotations; diff --git a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs index 3366f3d70f..a1749e4466 100644 --- a/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs +++ b/framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/AbpLdapModule.cs @@ -21,11 +21,7 @@ namespace Volo.Abp.Ldap context.Services.AddAbpDynamicOptions(); var configuration = context.Services.GetConfiguration(); - var ldapConfiguration = configuration["Ldap"]; - if (!ldapConfiguration.IsNullOrEmpty()) - { - Configure(configuration.GetSection("Ldap")); - } + Configure(configuration.GetSection("Ldap")); Configure(options => {