diff --git a/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md index 1a6c7bdde4..cf587b0e35 100644 --- a/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md +++ b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/POST.md @@ -1,12 +1,12 @@ # ABP Now Supports Keyed Services! -In this post, I describe the new **"keyed service"** support for the dependency injection container, which came with .NET 8.0. Then, I'll show you an example usage within ABP Framework. +In this post, I describe the new **"keyed service"** support for the dependency injection container, which came with .NET 8.0. Then, I'll show you an example of usage within the ABP Framework. ## What Are Keyed Services? -ASP.NET Core ships with a built-in dependency injection container, which is a pretty basic DI container that supports minimal features a dependency injection container supposed to have. For that reason, most of the .NET users use third-party containers like [Autofac](https://autofac.org/), or Ninject. +ASP.NET Core ships with a built-in dependency injection container, which is a pretty basic DI container that supports minimal features a dependency injection container is supposed to have. For that reason, most of the .NET users use third-party containers like [Autofac](https://autofac.org/), or Ninject. -> ABP Framework uses Autofac by default in startup templates, and it supports superior features. For example, the built-in DI container does not natively support property injection but Autofac does. +> ABP Framework uses Autofac by default in startup templates, and it supports more features. For example, the built-in DI container does not natively support property injection but Autofac does. [**Keyed dependency injection (DI) services**](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#keyed-services) were added to the built-in DI container as a new feature with .NET 8.0. This is an important feature, which allows for registering and retrieving DI services using keys/names. @@ -39,7 +39,7 @@ There was not a simple way to retrieve a specific implementation type directly. Autofac was already supporting the named/keyed DI services for a long time and with v9.0.0, they make it compatible with `Microsoft.Extensions.DependencyInjection` package (including the keyed service support). -After the v9.0.0 released for Autofac, then the ABP Framework Core team immediately updated the `Autofac.Extensions.DependencyInjection` package to `v9.0.0`, made the related changes in its own Autofac project and included in the v8.0.2 release. +After the v9.0.0 was released for Autofac, then the ABP Framework Core team immediately updated the `Autofac.Extensions.DependencyInjection` package to `v9.0.0`, made the related changes in its own Autofac project and included in the v8.0.2 release. You can see the related changes in the following PRs: @@ -50,19 +50,19 @@ You can see the related changes in the following PRs: After the quick background of the process and the feature itself, now let's see the keyed services in action. -First, update the ABP CLI to v8.0.2 with the following command: +First, update the ABP CLI to v8.0.2 with the following command (if your CLI version is newer, no need to apply this): ```bash dotnet tool update -g Volo.Abp.Cli --version 8.0.2 ``` -Then, we can create an application template (single-layer) with the following command (MVC as UI option and EF Core as the DB provider): +Then, we can create an application template (single-layer) with the following command (MVC as the UI option and EF Core as the DB provider): ```bash abp new KeyedServiceDemo -t app-nolayers -csf --version 8.0.2 ``` -After the application is created, we can open it in a IDE and start developing... +After the application is created, we can open it in an IDE and start developing... **Example**: @@ -90,7 +90,7 @@ public class MyServiceThree : IMyService } ``` -After created our services, now we can register them in our module class. So, open the module class and add the following lines in the `ConfigureServices` method: +After creating our services, now we can register them in our module class. So, open the module class and add the following lines in the `ConfigureServices` method: ```csharp public override void ConfigureServices(ServiceConfigurationContext context) @@ -104,30 +104,30 @@ After created our services, now we can register them in our module class. So, op ``` * To register a keyed service, you can use one of the `AddKeyedTransient`, `AddKeyedScoped`, or `AddKeyedSingleton` overloads, and provide a key for the registration. -* In the example above, I used `AddKeyedTransient` overload, and register all of these services as in the `Transient` lifetime. +* In the example above, I used `AddKeyedTransient` overload, and registered all of these services as in the `Transient` lifetime. Then, when you want to retrieve a keyed service, you can use the `[FromKeyedServices(object key)]` attribute: ```csharp -public class NotificationService([FromKeyedServices("myserviceone)] IMyService myServiceOne) +public class NotificationService([FromKeyedServices("myserviceone")] IMyService myServiceOne) { public string Notify() => myServiceOne.GetMessage(); } ``` -With this kind of use, you can be certain that the not the latest registered service is used and instead the keyed service will be resolved and then you can use it. +With this kind of use, you can be certain that not the latest registered service is resolved and instead the keyed service will be resolved, and then you can use it. -Also, you can use the `FromKeyedServicesAttribute` with minimal APIs as follows (in the module class): +Also, you can use the `FromKeyedServicesAttribute` with minimal APIs as follows: ```csharp public override void OnApplicationInitialization(ApplicationInitializationContext context) { //code abbreviation for clarity... - app.UseConfiguredEndpoints(endpoints => - { + app.UseConfiguredEndpoints(endpoints => + { endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IMyService myservice) => myservice.GetMessage()); - }); + }); } ``` @@ -135,7 +135,7 @@ That's how it's easy to register multiple services in a keyed fashion and resolv ## Advanced -In this section, I want to briefly mention some advanced topics, such as what happens if I register more than one service with the same key, or is there a way to get the keyed services from the `ServiceProvider` etc. +In this section, I want to briefly mention some advanced topics (relatively 😀), such as what happens if I register more than one service with the same key, or is there a way to get the keyed services from the `ServiceProvider` etc. ### Registering Multiple Services with the Same Key @@ -154,42 +154,44 @@ Let's assume that you mistakenly registered multiple services with the same key: In that case, when you try to resolve the dependency with the key, then the last registered one will be used without having a problem: ```csharp - //it will resolve the `MyServiceTwo` service! - endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IMyService myservice) => myservice.GetMessage()); +//it will resolve the `MyServiceTwo` service! +endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IMyService myservice) => myservice.GetMessage()); ``` Also, since you used the same key for multiple services, then you can inject the `IEnumerable` to get all services for a certain key and use them: ```csharp - //it will resolve both `MyServiceOne` and `MyServiceTwo` services! - endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IEnumerable myservices) => - { - var sb = new StringBuilder(); - foreach (var myService in myservices) - { - sb.AppendLine(myService.GetMessage()); - } - - return sb.ToString(); - }); +//it will resolve both `MyServiceOne` and `MyServiceTwo` services! +endpoints.MapGet("/my-service", ([FromKeyedServices("myserviceone")] IEnumerable myservices) => +{ + var sb = new StringBuilder(); + foreach (var myService in myservices) + { + sb.AppendLine(myService.GetMessage()); + } + + return sb.ToString(); +}); ``` ### Resolving Keyed Services from ServiceProvider & LazyServiceProvider -You can resolve keyed services by using the one of the extension methods (`.GetKeyedServices`, `.GetKeyedService<>`, `.GetRequiredKeyedService<>`, and others...): +You can resolve keyed services by using one of the extension methods (`.GetKeyedServices`, `.GetKeyedService<>`, `.GetRequiredKeyedService<>`, ...): ```csharp //uses the `MyServiceTwo` service! var myService = ServiceProvider.GetRequiredKeyedService("myserviceone"); ``` -On the other hand, resolving keyed services from `LazyServiceProvider` is not supported in v8.0.2, but there is a PR, which you can found at https://github.com/abpframework/abp/pull/18792, it will fix this problem and it will be included in the next version. +On the other hand, resolving keyed services from `LazyServiceProvider` is not supported in v8.0.2, but there is a PR, which you can find at https://github.com/abpframework/abp/pull/18792, it will fix this problem and it will be included in the next version. ### Automatically Registering Keyed Services -Currently, if you want to register a keyed service, you need to do it manually as we see in the previous sections by using one of the overloads (`.AddKeyedTransient`, `.AddKeyedScoped` and `.AddKeyedSingleton`). It would be good if we can make this process automatically and not need to manually register services, and that purpose I have [created an issue](https://github.com/abpframework/abp/issues/18794) that aims to introducing an attribute, which allows us to automatically register multiple services as keyed services. +Currently, if you want to register a keyed service, you need to do it manually as we see in the previous sections by using one of the overloads (`.AddKeyedTransient`, `.AddKeyedScoped` and `.AddKeyedSingleton`). + +It would be good if we could make this process automatically and not need to manually register services, and for that purpose, I have [created an issue](https://github.com/abpframework/abp/issues/18794) that aims to introduce an attribute, which allows us to automatically register multiple services as keyed services. -You can follow the issue, if you are considering to use keyed services in your application and want to register them automatically. +You can [follow the issue](https://github.com/abpframework/abp/issues/18794) if you are considering using keyed services in your application and don't want to register them manually. ## Summary diff --git a/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png new file mode 100644 index 0000000000..56b04d353c Binary files /dev/null and b/docs/en/Community-Articles/2024-01-18-ABP-Now-Supports-Keyed-Services/cover-image.png differ