From 517e27dd0a7682bdb5dd5eb11bcc8b37497cba6f Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Mon, 15 Jan 2024 22:38:23 +0300 Subject: [PATCH 1/4] Resolving the keys for the keyed services --- .../DependencyInjection/Keys.cs | 10 ++++ .../OrchardCoreServiceProviderFactory.cs | 46 +++++++++++++++++++ .../OrchardCoreServiceProviderFactoryTests.cs | 30 ++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Keys.cs create mode 100644 src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/OrchardCoreServiceProviderFactory.cs create mode 100644 test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs diff --git a/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Keys.cs b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Keys.cs new file mode 100644 index 00000000000..ae5fce581e5 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Keys.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace OrchardCore.DependencyInjection; + +public class Keys : List +{ + public Keys(IEnumerable collection) : base(collection) + { + } +} diff --git a/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/OrchardCoreServiceProviderFactory.cs b/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/OrchardCoreServiceProviderFactory.cs new file mode 100644 index 00000000000..13def687ccb --- /dev/null +++ b/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/OrchardCoreServiceProviderFactory.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; + +namespace OrchardCore.DependencyInjection; + +public class OrchardCoreServiceProviderFactory : IServiceProviderFactory +{ + public IServiceCollection CreateBuilder(IServiceCollection services) => services; + + public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) + { + var keyedDictionary = new Dictionary>(); + + foreach (var service in containerBuilder) + { + if (service.ServiceKey != null) + { + if (!keyedDictionary.TryGetValue(service.ServiceType, out var keysList)) + { + keysList = []; + keyedDictionary[service.ServiceType] = keysList; + } + + keysList.Add(service.ServiceKey); + } + } + + AddKeysService(containerBuilder, keyedDictionary); + + containerBuilder.AddSingleton(typeof(Keys<>)); + + return containerBuilder.BuildServiceProvider(); + } + + private static void AddKeysService(IServiceCollection services, IDictionary> keyedDictionary) + { + foreach (var keyedDictionaryItem in keyedDictionary) + { + var serviceType = typeof(Keys<>).MakeGenericType(keyedDictionaryItem.Key); + var service = Activator.CreateInstance(serviceType, keyedDictionaryItem.Value); + + services.AddSingleton(serviceType, service!); + } + } +} diff --git a/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs b/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs new file mode 100644 index 00000000000..90d325283c7 --- /dev/null +++ b/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs @@ -0,0 +1,30 @@ +namespace OrchardCore.DependencyInjection.Tests; + +public class OrchardCoreServiceProviderFactoryTests +{ + [Fact] + public void CanResolveKeys() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(new FooService()); + services.AddKeyedSingleton("foo", new FooService()); + services.AddKeyedSingleton("bar", new FooService()); + services.AddKeyedSingleton("baz", new FooService()); + + // Act + var serviceProviderFactory = new OrchardCoreServiceProviderFactory(); + var serviceProvider = serviceProviderFactory.CreateServiceProvider(services); + + // Assert + var keys = serviceProvider.GetRequiredService>(); + Assert.Equal(3, keys.Count); + Assert.Equal("foo", keys[0]); + Assert.Equal("bar", keys[1]); + Assert.Equal("baz", keys[2]); + } + + public class FooService + { + } +} From d9aaf68849dab61fb6bb158017278c21041a2578 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Mon, 15 Jan 2024 23:33:02 +0300 Subject: [PATCH 2/4] Add GetKeyedServiceDictionary() extension method --- .../Extensions/ServiceProviderExtensions.cs | 26 ++++++++++++++++ .../ServiceProviderExtensionsTests.cs | 30 +++++++++++++++++++ .../DependencyInjection/FooService.cs | 5 ++++ .../OrchardCoreServiceProviderFactoryTests.cs | 6 +--- 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Extensions/ServiceProviderExtensions.cs create mode 100644 test/OrchardCore.Tests/DependencyInjection/Extensions/ServiceProviderExtensionsTests.cs create mode 100644 test/OrchardCore.Tests/DependencyInjection/FooService.cs diff --git a/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Extensions/ServiceProviderExtensions.cs b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Extensions/ServiceProviderExtensions.cs new file mode 100644 index 00000000000..e672f3c7b99 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Infrastructure.Abstractions/DependencyInjection/Extensions/ServiceProviderExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using OrchardCore.DependencyInjection; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class ServiceProviderExtensions +{ + public static IReadOnlyDictionary GetKeyedServiceDictionary(this IServiceProvider serviceProvider) + { + var keys = serviceProvider.GetRequiredService>(); + + var keyedDictionary = new Dictionary(keys.Count); + var keyedDictionaryWrapper = new ReadOnlyDictionary(keyedDictionary); + + foreach (var key in keys) + { + var service = serviceProvider.GetKeyedService(key); + + keyedDictionary.Add(key, service); + } + + return keyedDictionaryWrapper; + } +} diff --git a/test/OrchardCore.Tests/DependencyInjection/Extensions/ServiceProviderExtensionsTests.cs b/test/OrchardCore.Tests/DependencyInjection/Extensions/ServiceProviderExtensionsTests.cs new file mode 100644 index 00000000000..dd47bd4628a --- /dev/null +++ b/test/OrchardCore.Tests/DependencyInjection/Extensions/ServiceProviderExtensionsTests.cs @@ -0,0 +1,30 @@ +namespace OrchardCore.DependencyInjection.Tests; + +public class ServiceProviderExtensionsTests +{ + [Fact] + public void ResolveKeyedServiceDictionary() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(new FooService()); + services.AddKeyedSingleton("foo", new FooService()); + services.AddKeyedSingleton("bar", new FooService()); + services.AddKeyedSingleton("baz", new FooService()); + + var serviceProviderFactory = new OrchardCoreServiceProviderFactory(); + var serviceProvider = serviceProviderFactory.CreateServiceProvider(services); + + // Act + var keyedDictionary = serviceProvider.GetKeyedServiceDictionary(); + + // Assert + Assert.Equal(3, keyedDictionary.Count); + Assert.Contains(keyedDictionary, item => item.Key.Equals("foo")); + Assert.Contains(keyedDictionary, item => item.Key.Equals("bar")); + Assert.Contains(keyedDictionary, item => item.Key.Equals("baz")); + Assert.IsType(keyedDictionary["foo"]); + Assert.IsType(keyedDictionary["bar"]); + Assert.IsType(keyedDictionary["baz"]); + } +} diff --git a/test/OrchardCore.Tests/DependencyInjection/FooService.cs b/test/OrchardCore.Tests/DependencyInjection/FooService.cs new file mode 100644 index 00000000000..a8a775ccdf6 --- /dev/null +++ b/test/OrchardCore.Tests/DependencyInjection/FooService.cs @@ -0,0 +1,5 @@ +namespace OrchardCore.DependencyInjection.Tests; + +internal class FooService +{ +} diff --git a/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs b/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs index 90d325283c7..d8e08ff8d5b 100644 --- a/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs +++ b/test/OrchardCore.Tests/DependencyInjection/OrchardCoreServiceProviderFactoryTests.cs @@ -3,7 +3,7 @@ namespace OrchardCore.DependencyInjection.Tests; public class OrchardCoreServiceProviderFactoryTests { [Fact] - public void CanResolveKeys() + public void ResolveKeys() { // Arrange var services = new ServiceCollection(); @@ -23,8 +23,4 @@ public void CanResolveKeys() Assert.Equal("bar", keys[1]); Assert.Equal("baz", keys[2]); } - - public class FooService - { - } } From b473a830f1c862389238317eb167ba81f63b66d9 Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Tue, 16 Jan 2024 00:08:20 +0300 Subject: [PATCH 3/4] Add UseOrchardCoreHost() extension method --- src/OrchardCore.Cms.Web/Program.cs | 11 ++++++++++- .../Extensions/HostBuilderExtensions.cs | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/Extensions/HostBuilderExtensions.cs diff --git a/src/OrchardCore.Cms.Web/Program.cs b/src/OrchardCore.Cms.Web/Program.cs index 4b4d39392bd..7b57967184d 100644 --- a/src/OrchardCore.Cms.Web/Program.cs +++ b/src/OrchardCore.Cms.Web/Program.cs @@ -1,13 +1,22 @@ +using OrchardCore.Cms.Web; +using OrchardCore.DependencyInjection; using OrchardCore.Logging; var builder = WebApplication.CreateBuilder(args); -builder.Host.UseNLogHost(); +builder.Host + .UseOrchardCoreHost() + .UseNLogHost(); builder.Services .AddOrchardCms() .AddSetupFeatures("OrchardCore.AutoSetup"); +builder.Services + .AddKeyedTransient("foo") + .AddKeyedTransient("bar") + .AddKeyedTransient("baz"); + var app = builder.Build(); if (!app.Environment.IsDevelopment()) diff --git a/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/Extensions/HostBuilderExtensions.cs b/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/Extensions/HostBuilderExtensions.cs new file mode 100644 index 00000000000..a137b9495c7 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Infrastructure/DependencyInjection/Extensions/HostBuilderExtensions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Hosting; + +namespace OrchardCore.DependencyInjection; + +public static class HostBuilderExtensions +{ + public static IHostBuilder UseOrchardCoreHost(this IHostBuilder builder) + => builder.UseServiceProviderFactory(new OrchardCoreServiceProviderFactory()); +} From f31488baff68a5c586846526ccdbbb8ed6525c3a Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Tue, 16 Jan 2024 00:14:14 +0300 Subject: [PATCH 4/4] Remove unnecessary testing data --- src/OrchardCore.Cms.Web/Program.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/OrchardCore.Cms.Web/Program.cs b/src/OrchardCore.Cms.Web/Program.cs index 7b57967184d..a403a59ee79 100644 --- a/src/OrchardCore.Cms.Web/Program.cs +++ b/src/OrchardCore.Cms.Web/Program.cs @@ -1,4 +1,3 @@ -using OrchardCore.Cms.Web; using OrchardCore.DependencyInjection; using OrchardCore.Logging; @@ -12,11 +11,6 @@ .AddOrchardCms() .AddSetupFeatures("OrchardCore.AutoSetup"); -builder.Services - .AddKeyedTransient("foo") - .AddKeyedTransient("bar") - .AddKeyedTransient("baz"); - var app = builder.Build(); if (!app.Environment.IsDevelopment())