Skip to content

Commit

Permalink
Allowing more decoration methods to support open generic types.
Browse files Browse the repository at this point in the history
  • Loading branch information
DanHarltey authored and khellang committed May 23, 2022
1 parent df48b72 commit ffef7cd
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 118 deletions.
52 changes: 26 additions & 26 deletions src/Scrutor/Decoration/DecorationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
using System;

namespace Scrutor.Decoration
{
internal static class DecorationFactory
{
public static Decoration Create(Type serviceType, Type? decoratorType, Func<object, IServiceProvider, object>? decoratorFactory)
{
IDecorationStrategy strategy;

if (serviceType.IsOpenGeneric() && decoratorType is not null && decoratorType.IsOpenGeneric())
{
strategy = new OpenGenericDecorationStrategy(serviceType, decoratorType);
}
else
{
strategy = new ClosedTypeDecorationStrategy(serviceType, decoratorType, decoratorFactory);
}

return new Decoration(strategy);
}

public static Decoration Create<TService>(Type? decoratorType, Func<object, IServiceProvider, object>? decoratorFactory)
=> new(new ClosedTypeDecorationStrategy(typeof(TService), decoratorType, decoratorFactory));
}
}
using System;

namespace Scrutor.Decoration
{
internal static class DecorationFactory
{
public static Decoration Create(Type serviceType, Type? decoratorType, Func<object, IServiceProvider, object>? decoratorFactory)
{
IDecorationStrategy strategy;

if (serviceType.IsOpenGeneric())
{
strategy = new OpenGenericDecorationStrategy(serviceType, decoratorType, decoratorFactory);
}
else
{
strategy = new ClosedTypeDecorationStrategy(serviceType, decoratorType, decoratorFactory);
}

return new Decoration(strategy);
}

public static Decoration Create<TService>(Type? decoratorType, Func<object, IServiceProvider, object>? decoratorFactory)
=> new(new ClosedTypeDecorationStrategy(typeof(TService), decoratorType, decoratorFactory));
}
}
136 changes: 74 additions & 62 deletions src/Scrutor/Decoration/OpenGenericDecorationStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,62 +1,74 @@
using Microsoft.Extensions.DependencyInjection;
using System;

namespace Scrutor.Decoration
{
internal class OpenGenericDecorationStrategy : IDecorationStrategy
{
private readonly Type _serviceType;
private readonly Type _decoratorType;

public OpenGenericDecorationStrategy(Type serviceType, Type decoratorType)
{
_serviceType = serviceType;
_decoratorType = decoratorType;
}

public Type ServiceType => _serviceType;

public bool CanDecorate(Type serviceType)
{
var canHandle = serviceType.IsGenericType
&& (!serviceType.IsGenericTypeDefinition)
&& _serviceType.GetGenericTypeDefinition() == serviceType.GetGenericTypeDefinition()
&& HasCompatibleGenericArguments(serviceType);

return canHandle;
}

public Func<IServiceProvider, object> CreateDecorator(ServiceDescriptor descriptor)
{
var genericArguments = descriptor.ServiceType.GetGenericArguments();
var closedDecorator = _decoratorType.MakeGenericType(genericArguments);

return DecoratorInstanceFactory.Default(descriptor, closedDecorator);
}

private bool HasCompatibleGenericArguments(Type serviceType)
{
var canHandle = false;

if (_decoratorType is null)
{
canHandle = true;
}
else
{
var genericArguments = serviceType.GetGenericArguments();

try
{
_ = _decoratorType.MakeGenericType(genericArguments);
canHandle = true;
}
catch (ArgumentException)
{
}
}

return canHandle;
}
}
}
using Microsoft.Extensions.DependencyInjection;
using System;

namespace Scrutor.Decoration
{
internal sealed class OpenGenericDecorationStrategy : IDecorationStrategy
{
private readonly Type _serviceType;
private readonly Type? _decoratorType;
private readonly Func<object, IServiceProvider, object>? _decoratorFactory;

public OpenGenericDecorationStrategy(Type serviceType, Type? decoratorType, Func<object, IServiceProvider, object>? decoratorFactory)
{
_serviceType = serviceType;
_decoratorType = decoratorType;
_decoratorFactory = decoratorFactory;
}

public Type ServiceType => _serviceType;

public bool CanDecorate(Type serviceType)
{
var canHandle = serviceType.IsGenericType
&& (!serviceType.IsGenericTypeDefinition)
&& _serviceType.GetGenericTypeDefinition() == serviceType.GetGenericTypeDefinition()
&& HasCompatibleGenericArguments(serviceType);

return canHandle;
}

public Func<IServiceProvider, object> CreateDecorator(ServiceDescriptor descriptor)
{
if (_decoratorType is not null)
{
var genericArguments = descriptor.ServiceType.GetGenericArguments();
var closedDecorator = _decoratorType.MakeGenericType(genericArguments);

return DecoratorInstanceFactory.Default(descriptor, closedDecorator);
}

if (_decoratorFactory is not null)
{
return DecoratorInstanceFactory.Custom(descriptor, _decoratorFactory);
}

throw new InvalidOperationException($"Both serviceType and decoratorFactory can not be null.");
}

private bool HasCompatibleGenericArguments(Type serviceType)
{
var canHandle = false;

if (_decoratorType is null)
{
canHandle = true;
}
else
{
var genericArguments = serviceType.GetGenericArguments();

try
{
_ = _decoratorType.MakeGenericType(genericArguments);
canHandle = true;
}
catch (ArgumentException)
{
}
}

return canHandle;
}
}
}
33 changes: 3 additions & 30 deletions test/Scrutor.Tests/OpenGenericDecorationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,27 +128,6 @@ public void DecorationFunctionsDoSupportOpenGenericType()
{
sc => sc.Decorate(typeof(QueryHandler<,>), typeof(LoggingQueryHandler<,>)),
sc => sc.TryDecorate(typeof(QueryHandler<,>), typeof(LoggingQueryHandler<,>)),
};

foreach (var decorationFunction in allDecorationFunctions)
{
var provider = ConfigureProvider(services =>
{
services.AddSingleton<QueryHandler<MyQuery, MyResult>, MyQueryHandler>();
decorationFunction(services);
});

var instance = provider.GetRequiredService<QueryHandler<MyQuery, MyResult>>();
var decorator = Assert.IsType<LoggingQueryHandler<MyQuery, MyResult>>(instance);
Assert.IsType<MyQueryHandler>(decorator.Inner);
}
}

[Fact]
public void DecorationFunctionsDoNotSupportOpenGenericType()
{
var allDecorationFunctions = new Action<IServiceCollection>[]
{
sc => sc.Decorate(typeof(QueryHandler<,>), (object obj, IServiceProvider sp) => new LoggingQueryHandler<MyQuery, MyResult>((IQueryHandler<MyQuery, MyResult>)obj)),
sc => sc.TryDecorate(typeof(QueryHandler<,>), (object obj, IServiceProvider sp) => new LoggingQueryHandler<MyQuery, MyResult>((IQueryHandler<MyQuery, MyResult>)obj)),
sc => sc.Decorate(typeof(QueryHandler<,>), (object obj) => new LoggingQueryHandler<MyQuery, MyResult>((IQueryHandler<MyQuery, MyResult>)obj)),
Expand All @@ -160,18 +139,12 @@ public void DecorationFunctionsDoNotSupportOpenGenericType()
var provider = ConfigureProvider(services =>
{
services.AddSingleton<QueryHandler<MyQuery, MyResult>, MyQueryHandler>();
try
{
decorationFunction(services);
}
catch (MissingTypeRegistrationException)
{
}
decorationFunction(services);
});

var instance = provider.GetRequiredService<QueryHandler<MyQuery, MyResult>>();
_ = Assert.IsType<MyQueryHandler>(instance);
var decorator = Assert.IsType<LoggingQueryHandler<MyQuery, MyResult>>(instance);
Assert.IsType<MyQueryHandler>(decorator.Inner);
}
}

Expand Down

0 comments on commit ffef7cd

Please sign in to comment.