diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/AppCore.Extensions.DependencyInjection.Abstractions.csproj b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/AppCore.Extensions.DependencyInjection.Abstractions.csproj index c2f5bed..70671c9 100644 --- a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/AppCore.Extensions.DependencyInjection.Abstractions.csproj +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/AppCore.Extensions.DependencyInjection.Abstractions.csproj @@ -10,6 +10,9 @@ all + + all + diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityBuilder.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityBuilder.cs index 494bfb3..d5cbc2e 100644 --- a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityBuilder.cs +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityBuilder.cs @@ -2,6 +2,7 @@ // Copyright (c) 2018-2022 the AppCore .NET project. using System; +using System.Linq; using AppCore.Diagnostics; using AppCore.Extensions.DependencyInjection.Activator; using Microsoft.Extensions.DependencyInjection; @@ -35,6 +36,18 @@ internal FacilityBuilder(IServiceCollection services, IActivator activator, Type FacilityType = facilityType; } + private IFacilityExtension CreateExtension(Type extensionType) + { + Type contractType = extensionType.GetInterfaces() + .First(i => i.GetGenericTypeDefinition() == typeof(IFacilityExtension<>)) + .GenericTypeArguments[0]; + + Type extensionWrapperType = typeof(FacilityExtensionWrapper<>).MakeGenericType(contractType); + object extension = Activator.CreateInstance(extensionType); + + return (IFacilityExtension)System.Activator.CreateInstance(extensionWrapperType, extension); + } + /// /// Adds an extension to the facility. /// @@ -49,7 +62,7 @@ public FacilityBuilder AddExtension(Type extensionType) Ensure.Arg.NotNull(extensionType); Ensure.Arg.OfType(extensionType, typeof(IFacilityExtension<>).MakeGenericType(FacilityType)); - var extension = (IFacilityExtension)Activator.CreateInstance(extensionType); + IFacilityExtension extension = CreateExtension(extensionType); extension.ConfigureServices(Services); return this; } @@ -107,9 +120,9 @@ internal FacilityBuilder(IServiceCollection services, IActivator activator) /// The type of the extension. /// The to allow chaining. public FacilityBuilder AddExtension() - where TExtension : IFacilityExtension + where TExtension : class, IFacilityExtension { - var extension = (IFacilityExtension)Activator.CreateInstance(typeof(TExtension)); + IFacilityExtension extension = Activator.CreateInstance(); extension.ConfigureServices(Services); return this; } diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionReflectionBuilder.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionReflectionBuilder.cs index 680bff5..53c2775 100644 --- a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionReflectionBuilder.cs +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionReflectionBuilder.cs @@ -36,7 +36,11 @@ public IFacilityExtensionReflectionBuilder AddResolver(Action? configure = public IReadOnlyCollection> Resolve(Type facilityType) { - return _resolvers.SelectMany(s => s.Resolve(facilityType)) + Type[] facilityTypes = facilityType.GetTypesAssignableFrom() + .Where(t => t.GetInterfaces().Contains(typeof(IFacility))) + .ToArray(); + + return _resolvers.SelectMany(s => facilityTypes.SelectMany(s.Resolve)) .ToList() .AsReadOnly(); } diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionWrapper.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionWrapper.cs new file mode 100644 index 0000000..3991493 --- /dev/null +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/FacilityExtensionWrapper.cs @@ -0,0 +1,22 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2022 the AppCore .NET project. + +using Microsoft.Extensions.DependencyInjection; + +namespace AppCore.Extensions.DependencyInjection.Facilities; + +internal sealed class FacilityExtensionWrapper : IFacilityExtension + where TContract : IFacility +{ + private readonly IFacilityExtension _extension; + + public FacilityExtensionWrapper(IFacilityExtension extension) + { + _extension = extension; + } + + public void ConfigureServices(IServiceCollection services) + { + _extension.ConfigureServices(services); + } +} \ No newline at end of file diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/IFacilityExtension.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/IFacilityExtension.cs index 831a4b8..8edc7b4 100644 --- a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/IFacilityExtension.cs +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.Abstractions/Facilities/IFacilityExtension.cs @@ -8,7 +8,7 @@ namespace AppCore.Extensions.DependencyInjection.Facilities; /// /// Represents an extension for a facility. /// -public interface IFacilityExtension +public interface IFacilityExtension where T : IFacility { /// diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/AssemblyResolver.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/AssemblyResolver.cs index bb0e691..238dcfe 100644 --- a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/AssemblyResolver.cs +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/AssemblyResolver.cs @@ -114,6 +114,18 @@ IEnumerable IFacilityResolver.Resolve() .Select(facilityType => (IFacility) _activator.CreateInstance(facilityType)); } + private IFacilityExtension CreateExtension(Type extensionType) + { + Type contractType = extensionType.GetInterfaces() + .First(i => i.GetGenericTypeDefinition() == typeof(IFacilityExtension<>)) + .GenericTypeArguments[0]; + + Type extensionWrapperType = typeof(FacilityExtensionWrapper<>).MakeGenericType(contractType); + object extension = _activator.CreateInstance(extensionType); + + return (IFacilityExtension)System.Activator.CreateInstance(extensionWrapperType, extension); + } + /// IEnumerable> IFacilityExtensionResolver.Resolve(Type facilityType) { @@ -129,8 +141,6 @@ IEnumerable> IFacilityExtensionResolver.Resolve(Ty scanner.Filters.Add(filter); return scanner.ScanAssemblies() - .Select( - facilityExtensionType => - (IFacilityExtension)_activator.CreateInstance(facilityExtensionType)); + .Select(CreateExtension); } } \ No newline at end of file diff --git a/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/FacilityExtensionWrapper.cs b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/FacilityExtensionWrapper.cs new file mode 100644 index 0000000..08f18dc --- /dev/null +++ b/DependencyInjection/src/AppCore.Extensions.DependencyInjection.AssemblyExtensions/Facilities/FacilityExtensionWrapper.cs @@ -0,0 +1,22 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2022 the AppCore .NET project. + +using Microsoft.Extensions.DependencyInjection; + +namespace AppCore.Extensions.DependencyInjection.Facilities; + +internal sealed class FacilityExtensionWrapper : IFacilityExtension + where TContract : IFacility +{ + internal IFacilityExtension Extension { get; } + + public FacilityExtensionWrapper(IFacilityExtension extension) + { + Extension = extension; + } + + public void ConfigureServices(IServiceCollection services) + { + Extension.ConfigureServices(services); + } +} \ No newline at end of file diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.AssemblyExtensions.Tests/AssemblyResolverTests.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.AssemblyExtensions.Tests/AssemblyResolverTests.cs index 127b021..b215303 100644 --- a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.AssemblyExtensions.Tests/AssemblyResolverTests.cs +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.AssemblyExtensions.Tests/AssemblyResolverTests.cs @@ -49,8 +49,12 @@ public void ResolvesAllFacilityExtensions() IEnumerable> facilityExtensions = ((IFacilityExtensionResolver)resolver).Resolve(typeof(Facility1)); - facilityExtensions.Select(f => f.GetType()) + facilityExtensions.Should() + .AllBeOfType>(); + + facilityExtensions.OfType>() + .Select(e => e.Extension.GetType()) .Should() - .BeEquivalentTo(new [] { typeof(Facility1Extension1), typeof(Facility1Extension2) }); + .BeEquivalentTo(new[] { typeof(Facility1Extension1), typeof(Facility1Extension2) }); } } \ No newline at end of file diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityBuilderTests.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityBuilderTests.cs index a9c96be..d069e37 100644 --- a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityBuilderTests.cs +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityBuilderTests.cs @@ -2,6 +2,7 @@ // Copyright (c) 2018-2022 the AppCore .NET project. using System; +using System.Linq; using AppCore.Extensions.DependencyInjection.Activator; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; @@ -30,8 +31,19 @@ public void AddExtensionGenericTypeRegistersServices() { var builder = new FacilityBuilder(_services, _activator); builder.AddExtension(); + builder.AddExtension(); + _services.Select(sd => sd.ServiceType) + .Should() + .BeEquivalentTo(new [] { typeof(FacilityExtensionTestService), typeof(FacilityExtensionTestService) }); + } + + [Fact] + public void AddExtensionForContractGenericTypeRegistersServices() + { + var builder = new FacilityBuilder(_services, _activator); + builder.AddExtension(); _services.Should() - .ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService)); + .Contain(sd => sd.ServiceType == typeof(FacilityExtensionTestService)); } [Fact] @@ -39,8 +51,10 @@ public void AddExtensionTypeRegistersServices() { var builder = new FacilityBuilder(_services, _activator); builder.AddExtension(typeof(TestFacilityExtension)); - _services.Should() - .ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService)); + builder.AddExtension(typeof(TestFacilityContractExtension)); + _services.Select(sd => sd.ServiceType) + .Should() + .BeEquivalentTo(new [] { typeof(FacilityExtensionTestService), typeof(FacilityExtensionTestService) }); } [Fact] @@ -48,20 +62,24 @@ public void AddExtensionRegistersServices() { var builder = new FacilityBuilder(_services, _activator); builder.AddExtension(new TestFacilityExtension()); - _services.Should() - .ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService)); + builder.AddExtension(new TestFacilityContractExtension()); + + _services.Select(sd => sd.ServiceType) + .Should() + .BeEquivalentTo(new [] { typeof(FacilityExtensionTestService), typeof(FacilityExtensionTestService) }); } [Fact] - public void AddExtensionsFromRegistersServices() + public void AddExtensionsFromInvokesResolver() { var resolver = Substitute.For(); - resolver.Resolve(Arg.Is(typeof(TestFacility))) - .Returns(new[] { new TestFacilityExtension() }); + resolver.Resolve(Arg.Any()) + .Returns(Array.Empty>()); var builder = new FacilityBuilder(_services, _activator); builder.AddExtensionsFrom(r => r.AddResolver(resolver)); - _services.Should() - .ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService)); + + resolver.Received() + .Resolve(typeof(TestFacility)); } } \ No newline at end of file diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityServiceCollectionExtensionsTests.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityServiceCollectionExtensionsTests.cs index a9a98f2..81ec1c7 100644 --- a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityServiceCollectionExtensionsTests.cs +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/FacilityServiceCollectionExtensionsTests.cs @@ -35,6 +35,14 @@ public void AddFacilityTypeRegistersServices() .ContainSingle(sd => sd.ServiceType == typeof(FacilityTestService)); } + [Fact] + public void AddFacilityTypeWithContractRegistersServices() + { + _services.AddFacility(typeof(TestFacility)); + + _services.Should() + .ContainSingle(sd => sd.ServiceType == typeof(FacilityTestService)); + } [Fact] public void AddFacilitiesFromRegistersServices() diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/ITestFacility.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/ITestFacility.cs new file mode 100644 index 0000000..3889798 --- /dev/null +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/ITestFacility.cs @@ -0,0 +1,8 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2022 the AppCore .NET project. + +namespace AppCore.Extensions.DependencyInjection.Facilities; + +public interface ITestFacility : IFacility +{ +} \ No newline at end of file diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacility.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacility.cs index 828f49e..ef4cc41 100644 --- a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacility.cs +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacility.cs @@ -5,7 +5,7 @@ namespace AppCore.Extensions.DependencyInjection.Facilities; -public class TestFacility : IFacility +public class TestFacility : ITestFacility { public void ConfigureServices(IServiceCollection services) { diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityContractExtension.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityContractExtension.cs new file mode 100644 index 0000000..e61631f --- /dev/null +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityContractExtension.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace AppCore.Extensions.DependencyInjection.Facilities; + +public class TestFacilityContractExtension : IFacilityExtension +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + } +} \ No newline at end of file diff --git a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityExtension.cs b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityExtension.cs index 5f55ff9..d89e32c 100644 --- a/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityExtension.cs +++ b/DependencyInjection/test/AppCore.Extensions.DependencyInjection.Tests/Facilities/TestFacilityExtension.cs @@ -10,6 +10,6 @@ public class TestFacilityExtension : IFacilityExtension { public void ConfigureServices(IServiceCollection services) { - services.TryAddTransient(); + services.AddTransient(); } } \ No newline at end of file diff --git a/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/FacilityExtensionWrapper.cs b/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/FacilityExtensionWrapper.cs new file mode 100644 index 0000000..0dc235e --- /dev/null +++ b/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/FacilityExtensionWrapper.cs @@ -0,0 +1,23 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2022 the AppCore .NET project. + +using Microsoft.Extensions.DependencyInjection; + +// ReSharper disable once CheckNamespace +namespace AppCore.Extensions.DependencyInjection.Facilities; + +internal sealed class FacilityExtensionWrapper : IFacilityExtension + where TContract : IFacility +{ + private readonly IFacilityExtension _extension; + + public FacilityExtensionWrapper(IFacilityExtension extension) + { + _extension = extension; + } + + public void ConfigureServices(IServiceCollection services) + { + _extension.ConfigureServices(services); + } +} \ No newline at end of file diff --git a/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/PluginFacilityResolver.cs b/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/PluginFacilityResolver.cs index daf7bae..9fd0659 100644 --- a/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/PluginFacilityResolver.cs +++ b/Hosting/src/AppCore.Extensions.Hosting.Plugins/DependencyInjection/PluginFacilityResolver.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using AppCore.Diagnostics; using AppCore.Extensions.Hosting.Plugins; @@ -35,14 +37,30 @@ IEnumerable IFacilityResolver.Resolve() } } + private IFacilityExtension CreateExtension(IPluginService pluginService) + { + Type extensionType = pluginService.GetType() + .GetInterfaces() + .First(i => i.GetGenericTypeDefinition() == typeof(IPluginService<>)) + .GenericTypeArguments[0]; + + Type contractType = extensionType.GenericTypeArguments[0]; + Type extensionWrapperType = typeof(FacilityExtensionWrapper<>).MakeGenericType(contractType); + + return (IFacilityExtension)System.Activator.CreateInstance( + extensionWrapperType, + pluginService.Instance)!; + } + /// IEnumerable> IFacilityExtensionResolver.Resolve(Type facilityType) { - foreach (IPluginService pluginService in _pluginManager.GetServices( - typeof(IFacilityExtension<>).MakeGenericType(facilityType))) + IPluginServiceCollection services = _pluginManager.GetServices( + typeof(IFacilityExtension<>).MakeGenericType(facilityType)); + + foreach (IPluginService service in services) { - var facility = (IPluginService>)pluginService; - yield return facility.Instance; + yield return CreateExtension(service); } } } \ No newline at end of file diff --git a/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/ITestFacility.cs b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/ITestFacility.cs new file mode 100644 index 0000000..322e346 --- /dev/null +++ b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/ITestFacility.cs @@ -0,0 +1,10 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2021 the AppCore .NET project. + +using AppCore.Extensions.DependencyInjection.Facilities; + +namespace AppCore.Extensions.Hosting.Plugins.TestPlugin; + +public interface ITestFacility : IFacility +{ +} \ No newline at end of file diff --git a/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacility.cs b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacility.cs index 5823800..93f92cb 100644 --- a/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacility.cs +++ b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacility.cs @@ -1,12 +1,11 @@ // Licensed under the MIT License. // Copyright (c) 2018-2021 the AppCore .NET project. -using AppCore.Extensions.DependencyInjection.Facilities; using Microsoft.Extensions.DependencyInjection; namespace AppCore.Extensions.Hosting.Plugins.TestPlugin; -public class TestFacility : IFacility +public class TestFacility : ITestFacility { public void ConfigureServices(IServiceCollection services) { diff --git a/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtension.cs b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtension.cs new file mode 100644 index 0000000..1c5082a --- /dev/null +++ b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtension.cs @@ -0,0 +1,15 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2021 the AppCore .NET project. + +using AppCore.Extensions.DependencyInjection.Facilities; +using Microsoft.Extensions.DependencyInjection; + +namespace AppCore.Extensions.Hosting.Plugins.TestPlugin; + +public class TestFacilityContractExtension : IFacilityExtension +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + } +} \ No newline at end of file diff --git a/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtensionService.cs b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtensionService.cs new file mode 100644 index 0000000..d35fde2 --- /dev/null +++ b/Hosting/test/AppCore.Extensions.Hosting.Plugins.TestPlugin/TestFacilityContractExtensionService.cs @@ -0,0 +1,8 @@ +// Licensed under the MIT License. +// Copyright (c) 2018-2021 the AppCore .NET project. + +namespace AppCore.Extensions.Hosting.Plugins.TestPlugin; + +public class TestFacilityContractExtensionService +{ +} \ No newline at end of file diff --git a/Hosting/test/AppCore.Extensions.Hosting.Plugins.Tests/PluginFacilityResolverTests.cs b/Hosting/test/AppCore.Extensions.Hosting.Plugins.Tests/PluginFacilityResolverTests.cs index 2edce64..6577013 100644 --- a/Hosting/test/AppCore.Extensions.Hosting.Plugins.Tests/PluginFacilityResolverTests.cs +++ b/Hosting/test/AppCore.Extensions.Hosting.Plugins.Tests/PluginFacilityResolverTests.cs @@ -31,14 +31,26 @@ public void RegistersFacilitiesWithExtensions() }); services.AddFacilitiesFrom( - s => s.Plugins() - .AddExtensionsFrom(r => r.Plugins())); + s => s + .Plugins() + .AddExtensionsFrom(r => r.Plugins())); services.Should() .Contain( r => - r.ServiceType.FullName == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityExtensionService" - && r.ImplementationType.FullName == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityExtensionService" + r.ServiceType.FullName + == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityExtensionService" + && r.ImplementationType.FullName + == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityExtensionService" + ); + + services.Should() + .Contain( + r => + r.ServiceType.FullName + == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityContractExtensionService" + && r.ImplementationType.FullName + == "AppCore.Extensions.Hosting.Plugins.TestPlugin.TestFacilityContractExtensionService" ); services.Should()