Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,9 @@
<PackageReference Include="AppCore.Diagnostics.Sources" Version="$(AppCore_Shared_Version)">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="AppCore.TypeHelpers.Sources" Version="$(AppCore_Shared_Version)">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(Microsoft_Extensions_Version)" />
</ItemGroup>

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -35,6 +36,18 @@ internal FacilityBuilder(IServiceCollection services, IActivator activator, Type
FacilityType = facilityType;
}

private IFacilityExtension<IFacility> 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<IFacility>)System.Activator.CreateInstance(extensionWrapperType, extension);
}

/// <summary>
/// Adds an extension to the facility.
/// </summary>
Expand All@@ -49,7 +62,7 @@ public FacilityBuilder AddExtension(Type extensionType)
Ensure.Arg.NotNull(extensionType);
Ensure.Arg.OfType(extensionType, typeof(IFacilityExtension<>).MakeGenericType(FacilityType));

var extension = (IFacilityExtension<IFacility>)Activator.CreateInstance(extensionType);
IFacilityExtension<IFacility> extension = CreateExtension(extensionType);
extension.ConfigureServices(Services);
return this;
}
Expand DownExpand Up@@ -107,9 +120,9 @@ internal FacilityBuilder(IServiceCollection services, IActivator activator)
/// <typeparam name="TExtension">The type of the extension.</typeparam>
/// <returns>The <see cref="FacilityBuilder{T}"/> to allow chaining.</returns>
public FacilityBuilder<T> AddExtension<TExtension>()
where TExtension : IFacilityExtension<T>
where TExtension : class, IFacilityExtension<T>
{
var extension = (IFacilityExtension<IFacility>)Activator.CreateInstance(typeof(TExtension));
IFacilityExtension<T> extension = Activator.CreateInstance<TExtension>();
extension.ConfigureServices(Services);
return this;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,7 +36,11 @@ public IFacilityExtensionReflectionBuilder AddResolver<T>(Action<T>? configure =

public IReadOnlyCollection<IFacilityExtension<IFacility>> 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();
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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<TContract> : IFacilityExtension<IFacility>
where TContract : IFacility
{
private readonly IFacilityExtension<TContract> _extension;

public FacilityExtensionWrapper(IFacilityExtension<TContract> extension)
{
_extension = extension;
}

public void ConfigureServices(IServiceCollection services)
{
_extension.ConfigureServices(services);
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@ namespace AppCore.Extensions.DependencyInjection.Facilities;
/// <summary>
/// Represents an extension for a facility.
/// </summary>
public interface IFacilityExtension<out T>
public interface IFacilityExtension<in T>
where T : IFacility
{
/// <summary>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,6 +114,18 @@ IEnumerable<IFacility> IFacilityResolver.Resolve()
.Select(facilityType => (IFacility) _activator.CreateInstance(facilityType));
}

private IFacilityExtension<IFacility> 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<IFacility>)System.Activator.CreateInstance(extensionWrapperType, extension);
}

/// <inheritdoc />
IEnumerable<IFacilityExtension<IFacility>> IFacilityExtensionResolver.Resolve(Type facilityType)
{
Expand All@@ -129,8 +141,6 @@ IEnumerable<IFacilityExtension<IFacility>> IFacilityExtensionResolver.Resolve(Ty
scanner.Filters.Add(filter);

return scanner.ScanAssemblies()
.Select(
facilityExtensionType =>
(IFacilityExtension<IFacility>)_activator.CreateInstance(facilityExtensionType));
.Select(CreateExtension);
}
}
Original file line numberDiff line numberDiff line change
@@ -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<TContract> : IFacilityExtension<IFacility>
where TContract : IFacility
{
internal IFacilityExtension<TContract> Extension { get; }

public FacilityExtensionWrapper(IFacilityExtension<TContract> extension)
{
Extension = extension;
}

public void ConfigureServices(IServiceCollection services)
{
Extension.ConfigureServices(services);
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,8 +49,12 @@ public void ResolvesAllFacilityExtensions()
IEnumerable<IFacilityExtension<IFacility>> facilityExtensions =
((IFacilityExtensionResolver)resolver).Resolve(typeof(Facility1));

facilityExtensions.Select(f => f.GetType())
facilityExtensions.Should()
.AllBeOfType<FacilityExtensionWrapper<Facility1>>();

facilityExtensions.OfType<FacilityExtensionWrapper<Facility1>>()
.Select(e => e.Extension.GetType())
.Should()
.BeEquivalentTo(new[] { typeof(Facility1Extension1), typeof(Facility1Extension2) });
.BeEquivalentTo(new[] { typeof(Facility1Extension1), typeof(Facility1Extension2) });
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -30,38 +31,55 @@ public void AddExtensionGenericTypeRegistersServices()
{
var builder = new FacilityBuilder<TestFacility>(_services, _activator);
builder.AddExtension<TestFacilityExtension>();
builder.AddExtension<TestFacilityContractExtension>();
_services.Select(sd => sd.ServiceType)
.Should()
.BeEquivalentTo(new [] { typeof(FacilityExtensionTestService), typeof(FacilityExtensionTestService) });
}

[Fact]
public void AddExtensionForContractGenericTypeRegistersServices()
{
var builder = new FacilityBuilder<ITestFacility>(_services, _activator);
builder.AddExtension<TestFacilityContractExtension>();
_services.Should()
.ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService));
.Contain(sd => sd.ServiceType == typeof(FacilityExtensionTestService));
}

[Fact]
public void AddExtensionTypeRegistersServices()
{
var builder = new FacilityBuilder<TestFacility>(_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]
public void AddExtensionRegistersServices()
{
var builder = new FacilityBuilder<TestFacility>(_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<IFacilityExtensionResolver>();
resolver.Resolve(Arg.Is(typeof(TestFacility)))
.Returns(new[] { new TestFacilityExtension() });
resolver.Resolve(Arg.Any<Type>())
.Returns(Array.Empty<IFacilityExtension<IFacility>>());

var builder = new FacilityBuilder<TestFacility>(_services, _activator);
builder.AddExtensionsFrom(r => r.AddResolver(resolver));
_services.Should()
.ContainSingle(sd => sd.ServiceType == typeof(FacilityExtensionTestService));

resolver.Received()
.Resolve(typeof(TestFacility));
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -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()
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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
{
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

namespace AppCore.Extensions.DependencyInjection.Facilities;

public class TestFacility : IFacility
public class TestFacility : ITestFacility
{
public void ConfigureServices(IServiceCollection services)
{
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace AppCore.Extensions.DependencyInjection.Facilities;

public class TestFacilityContractExtension : IFacilityExtension<ITestFacility>
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<FacilityExtensionTestService>();
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,6 @@ public class TestFacilityExtension : IFacilityExtension<TestFacility>
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddTransient<FacilityExtensionTestService>();
services.AddTransient<FacilityExtensionTestService>();
}
}
Original file line numberDiff line numberDiff line change
@@ -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<TContract> : IFacilityExtension<IFacility>
where TContract : IFacility
{
private readonly IFacilityExtension<TContract> _extension;

public FacilityExtensionWrapper(IFacilityExtension<TContract> extension)
{
_extension = extension;
}

public void ConfigureServices(IServiceCollection services)
{
_extension.ConfigureServices(services);
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AppCore.Diagnostics;
using AppCore.Extensions.Hosting.Plugins;

Expand DownExpand Up@@ -35,14 +37,30 @@ IEnumerable<IFacility> IFacilityResolver.Resolve()
}
}

private IFacilityExtension<IFacility> CreateExtension(IPluginService<object> 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<IFacility>)System.Activator.CreateInstance(
extensionWrapperType,
pluginService.Instance)!;
}

/// <inheritdoc />
IEnumerable<IFacilityExtension<IFacility>> IFacilityExtensionResolver.Resolve(Type facilityType)
{
foreach (IPluginService<object> pluginService in _pluginManager.GetServices(
typeof(IFacilityExtension<>).MakeGenericType(facilityType)))
IPluginServiceCollection<object> services = _pluginManager.GetServices(
typeof(IFacilityExtension<>).MakeGenericType(facilityType));

foreach (IPluginService<object> service in services)
{
var facility = (IPluginService<IFacilityExtension<IFacility>>)pluginService;
yield return facility.Instance;
yield return CreateExtension(service);
}
}
}
Original file line numberDiff line numberDiff line change
@@ -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
{
}
Loading