From 4a04fd9beb79b43071f7678f9363693666085c93 Mon Sep 17 00:00:00 2001 From: Kamil Zakiev Date: Thu, 6 Jun 2019 19:48:27 +0300 Subject: [PATCH 1/2] added open generic support for decorator func --- .../ServiceCollectionExtensions.Decoration.cs | 28 +++++++++++++- .../OpenGenericDecorationTests.cs | 38 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/Scrutor/ServiceCollectionExtensions.Decoration.cs b/src/Scrutor/ServiceCollectionExtensions.Decoration.cs index 83c5dee7..505e3955 100644 --- a/src/Scrutor/ServiceCollectionExtensions.Decoration.cs +++ b/src/Scrutor/ServiceCollectionExtensions.Decoration.cs @@ -170,6 +170,11 @@ public static IServiceCollection Decorate(this IServiceCollection services, Type Preconditions.NotNull(serviceType, nameof(serviceType)); Preconditions.NotNull(decorator, nameof(decorator)); + if (serviceType.IsOpenGeneric()) + { + return services.DecorateOpenGeneric(serviceType, decorator); + } + return services.DecorateDescriptors(serviceType, x => x.Decorate(decorator)); } @@ -243,10 +248,28 @@ private static bool IsSameGenericType(Type t1, Type t2) return t1.IsGenericType && t2.IsGenericType && t1.GetGenericTypeDefinition() == t2.GetGenericTypeDefinition(); } - private static bool TryDecorateOpenGeneric(this IServiceCollection services, Type serviceType, Type decoratorType) + private static IServiceCollection DecorateOpenGeneric(this IServiceCollection services, Type serviceType, Func decorator) { bool TryDecorate(Type[] typeArguments) { + var closedServiceType = serviceType.MakeGenericType(typeArguments); + return services.TryDecorateDescriptors(closedServiceType, x => x.Decorate(decorator)); + } + + if (services.TryDecorateOpenGeneric(serviceType, openTypeDecorator: TryDecorate)) + { + return services; + } + + throw new MissingTypeRegistrationException(serviceType); + } + + private static bool TryDecorateOpenGeneric(this IServiceCollection services, Type serviceType, Type decoratorType = null, Func openTypeDecorator = null) + { + bool TryDecorate(Type[] typeArguments) + { + Preconditions.NotNull(decoratorType, nameof(decoratorType)); + var closedServiceType = serviceType.MakeGenericType(typeArguments); var closedDecoratorType = decoratorType.MakeGenericType(typeArguments); @@ -263,7 +286,8 @@ bool TryDecorate(Type[] typeArguments) return false; } - return arguments.Aggregate(true, (result, args) => result && TryDecorate(args)); + var tryDecorate = openTypeDecorator ?? TryDecorate; + return arguments.Aggregate(true, (result, args) => result && tryDecorate(args)); } private static IServiceCollection DecorateDescriptors(this IServiceCollection services, Type serviceType, Func decorator) diff --git a/test/Scrutor.Tests/OpenGenericDecorationTests.cs b/test/Scrutor.Tests/OpenGenericDecorationTests.cs index c090766f..98e8e5ee 100644 --- a/test/Scrutor.Tests/OpenGenericDecorationTests.cs +++ b/test/Scrutor.Tests/OpenGenericDecorationTests.cs @@ -41,6 +41,28 @@ public void CanDecorateOpenGenericTypeBasedOnInterface() Assert.IsType(loggingDecorator.Inner); } + [Fact] + public void CanDecorateOpenGenericTypeBasedOnInterfaceByDecoratorFunc() + { + var provider = ConfigureProvider(services => + { + services.AddSingleton, MySpecialQueryHandler>(); + services.Decorate(typeof(IQueryHandler<,>), (handlerObj, serviceProvider) => + { + if (handlerObj is ISpecialInterface specialInterface) + { + specialInterface.InitSomeField(); + } + + return handlerObj; + }); + }); + + var instance = provider.GetRequiredService>(); + var myQueryHandler = Assert.IsType(instance); + Assert.True(myQueryHandler.GetSomeField()); + } + [Fact] public void DecoratingNonRegisteredOpenGenericServiceThrows() { @@ -79,6 +101,22 @@ public void DecoratingOpenGenericTypeBasedOnGrandparentInterfaceDoesNotDecorateP } } + public interface ISpecialInterface + { + void InitSomeField(); + } + + public class MySpecialQueryHandler : QueryHandler, ISpecialInterface + { + private bool _someField = false; + public void InitSomeField() + { + _someField = true; + } + + public bool GetSomeField() => _someField; + } + public class MyQuery { } public class MyResult { } From b26599151dfaab1aad96ff4eb994565da7c615a5 Mon Sep 17 00:00:00 2001 From: Kamil Zakiev Date: Fri, 7 Jun 2019 10:58:08 +0300 Subject: [PATCH 2/2] refactored TryDecorateOpenGeneric implementation --- .../ServiceCollectionExtensions.Decoration.cs | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/Scrutor/ServiceCollectionExtensions.Decoration.cs b/src/Scrutor/ServiceCollectionExtensions.Decoration.cs index 505e3955..bc34c450 100644 --- a/src/Scrutor/ServiceCollectionExtensions.Decoration.cs +++ b/src/Scrutor/ServiceCollectionExtensions.Decoration.cs @@ -78,7 +78,8 @@ public static bool TryDecorate(this IServiceCollection services, Type serviceTyp if (serviceType.IsOpenGeneric() && decoratorType.IsOpenGeneric()) { - return services.TryDecorateOpenGeneric(serviceType, decoratorType); + var openTypeTryDecorator = OpenTypeTryDecorator(services, serviceType, decoratorType); + return services.TryDecorateOpenGeneric(serviceType, openTypeTryDecorator); } return services.TryDecorateDescriptors(serviceType, x => x.Decorate(decoratorType)); @@ -235,7 +236,8 @@ public static bool TryDecorate(this IServiceCollection services, Type serviceTyp private static IServiceCollection DecorateOpenGeneric(this IServiceCollection services, Type serviceType, Type decoratorType) { - if (services.TryDecorateOpenGeneric(serviceType, decoratorType)) + var openTypeTryDecorator = OpenTypeTryDecorator(services, serviceType, decoratorType); + if (services.TryDecorateOpenGeneric(serviceType, openTypeTryDecorator)) { return services; } @@ -250,13 +252,8 @@ private static bool IsSameGenericType(Type t1, Type t2) private static IServiceCollection DecorateOpenGeneric(this IServiceCollection services, Type serviceType, Func decorator) { - bool TryDecorate(Type[] typeArguments) - { - var closedServiceType = serviceType.MakeGenericType(typeArguments); - return services.TryDecorateDescriptors(closedServiceType, x => x.Decorate(decorator)); - } - - if (services.TryDecorateOpenGeneric(serviceType, openTypeDecorator: TryDecorate)) + var openTypeTryDecorator = OpenTypeTryDecorator(services, serviceType, decorator); + if (services.TryDecorateOpenGeneric(serviceType, openTypeTryDecorator)) { return services; } @@ -264,18 +261,8 @@ bool TryDecorate(Type[] typeArguments) throw new MissingTypeRegistrationException(serviceType); } - private static bool TryDecorateOpenGeneric(this IServiceCollection services, Type serviceType, Type decoratorType = null, Func openTypeDecorator = null) + private static bool TryDecorateOpenGeneric(this IServiceCollection services, Type serviceType, Func openTypeTryDecorator) { - bool TryDecorate(Type[] typeArguments) - { - Preconditions.NotNull(decoratorType, nameof(decoratorType)); - - var closedServiceType = serviceType.MakeGenericType(typeArguments); - var closedDecoratorType = decoratorType.MakeGenericType(typeArguments); - - return services.TryDecorateDescriptors(closedServiceType, x => x.Decorate(closedDecoratorType)); - } - var arguments = services .Where(descriptor => IsSameGenericType(descriptor.ServiceType, serviceType)) .Select(descriptor => descriptor.ServiceType.GenericTypeArguments) @@ -286,8 +273,27 @@ bool TryDecorate(Type[] typeArguments) return false; } - var tryDecorate = openTypeDecorator ?? TryDecorate; - return arguments.Aggregate(true, (result, args) => result && tryDecorate(args)); + return arguments.Aggregate(true, (result, args) => result && openTypeTryDecorator(args)); + } + + private static Func OpenTypeTryDecorator(IServiceCollection services, Type serviceType, Type decoratorType) + { + return typeArguments => + { + var closedServiceType = serviceType.MakeGenericType(typeArguments); + var closedDecoratorType = decoratorType.MakeGenericType(typeArguments); + + return services.TryDecorateDescriptors(closedServiceType, x => x.Decorate(closedDecoratorType)); + }; + } + + private static Func OpenTypeTryDecorator(IServiceCollection services, Type serviceType, Func decorator) + { + return typeArguments => + { + var closedServiceType = serviceType.MakeGenericType(typeArguments); + return services.TryDecorateDescriptors(closedServiceType, x => x.Decorate(decorator)); + }; } private static IServiceCollection DecorateDescriptors(this IServiceCollection services, Type serviceType, Func decorator)