Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to register decorator classes? #149

Closed
davidkeaveny opened this issue Jan 31, 2018 · 9 comments
Closed

How to register decorator classes? #149

davidkeaveny opened this issue Jan 31, 2018 · 9 comments
Labels

Comments

@davidkeaveny
Copy link

If I have an abstract base class CommandHandler<T>, and I want to register a decorator class LoggingCommandHandler<T> such that every class that implements the base class is resolved with the decorator class, how do I tell Grace to achieve such a thing?

How about if I want to add another decorator class ValidatingCommandHandler<T> and I want the order of the decorator classes to be preserved? i.e. always resolve as:

LoggingCommandHandler
--> ValidatingCommandHandler
----> ConcreteCommandHandler
@ipjohnson
Copy link
Owner

Hi @davidkeaveny

I took a quick look and out of the box you can't do what you want to do but I think it's possible to do it with a C# extension.

I'll write up an example of how to do it and post a gist of it.

I also think this is a worth feature request so I'll see about making it part of Grace officially so you don't have to do an extension.

@ipjohnson
Copy link
Owner

@davidkeaveny did the unit test for dectorators work for you? Also what are your thoughts on C# extension vs a member on the interface?

@davidkeaveny
Copy link
Author

It works flawlessly 👍

Would the extension method basically replace the .When.MeetsCondition(new WhenDecoratingCondition(typeof(BaseCommand<>)))? It would certainly clean up the registration process a little more, but it's not essential.

@davidkeaveny
Copy link
Author

davidkeaveny commented Feb 21, 2018

Hmm, maybe I jumped the gun a little bit - the first decorator that I register works flawlessly (the LoggingCommandHandlerDecorator<> in my example) and all my command handlers are now logged;
but when I then add the registration for another decorator (the ValidatingCommandHandlerDecorator<>) then Grace fails to resolve the type.

I've tried switching it round, and get the same result - register one decorator, all is good; register two decorators, all is bad.

The error that Grace throws is:

System.ArgumentNullException: Value cannot be null.
Parameter name: type
   at System.Reflection.IntrospectionExtensions.GetTypeInfo(Type type)
   at Grace.DependencyInjection.Impl.Expressions.ActivationExpressionBuilder.GetValueFromRequest(IInjectionScope scope, IActivationExpressionRequest request, Type activationType, Object key)
   at Grace.DependencyInjection.Impl.Expressions.ActivationExpressionBuilder.GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request)
   at Grace.DependencyInjection.Impl.Expressions.ConstructorExpressionCreator.GetParameterExpression(ParameterInfo parameter, ConstructorParameterInfo parameterInfo, IInjectionScope injectionScope, TypeActivationConfiguration configuration, IActivationExpressionRequest request)
   at Grace.DependencyInjection.Impl.Expressions.ConstructorExpressionCreator.GetParameterExpressionsForConstructor(IInjectionScope injectionScope, TypeActivationConfiguration configuration, IActivationExpressionRequest request, ConstructorInfo constructor)
   at Grace.DependencyInjection.Impl.Expressions.ConstructorExpressionCreator.CreateExpression(IInjectionScope scope, IActivationExpressionRequest request, TypeActivationConfiguration activationConfiguration)
   at Grace.DependencyInjection.Impl.Expressions.InstantiationExpressionCreator.CreateExpression(IInjectionScope scope, IActivationExpressionRequest request, TypeActivationConfiguration configuration)
   at Grace.DependencyInjection.Impl.Expressions.TypeExpressionBuilder.GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request, TypeActivationConfiguration activationConfiguration)
   at Grace.DependencyInjection.Impl.Expressions.DefaultStrategyExpressionBuilder.GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request, TypeActivationConfiguration activationConfiguration, ICompiledLifestyle lifestyle)
   at Grace.DependencyInjection.Impl.CompiledStrategies.GenericCompiledDecoratorStrategy.GetDecoratorActivationExpression(IInjectionScope scope, IActivationExpressionRequest request, ICompiledLifestyle lifestyle)
   at Grace.DependencyInjection.Impl.Expressions.DecoratorActivationPathNode.GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request)
   at Grace.DependencyInjection.Impl.Expressions.ActivationExpressionBuilder.CreateDecoratedActivationStrategy(IInjectionScope scope, IActivationExpressionRequest request, ICompiledExportStrategy strategy, List`1 decorators)
   at Grace.DependencyInjection.Impl.Expressions.ActivationExpressionBuilder.DecorateExportStrategy(IInjectionScope scope, IActivationExpressionRequest request, ICompiledExportStrategy strategy)
   at Grace.DependencyInjection.Impl.CompiledStrategies.CompiledExportStrategy.GetActivationExpression(IInjectionScope scope, IActivationExpressionRequest request)
   at Grace.DependencyInjection.Impl.CompiledStrategies.CompiledExportStrategy.GetActivationStrategyDelegate(IInjectionScope scope, IActivationStrategyCompiler compiler, Type activationType)
   at Grace.DependencyInjection.Impl.ActivationStrategyCompiler.LocateStrategyFromCollectionContainers(IInjectionScope scope, Type locateType, ActivationStrategyFilter consider, Object key, IInjectionContext injectionContext)
   at Grace.DependencyInjection.Impl.ActivationStrategyCompiler.FindDelegate(IInjectionScope scope, Type locateType, ActivationStrategyFilter consider, Object key, IInjectionContext injectionContext, Boolean checkMissing)
   at Grace.DependencyInjection.Impl.InjectionScope.InternalLocate(IExportLocatorScope scope, IDisposalScope disposalScope, Type type, ActivationStrategyFilter consider, Object key, IInjectionContext injectionContext, Boolean allowNull, Boolean isDynamic)
   at Grace.DependencyInjection.Impl.InjectionScope.Locate(Type type)
   at Grace.DependencyInjection.Impl.InjectionScope.Locate[T]()

@ipjohnson
Copy link
Owner

Hmm, I can see the piece of code that must have failed to end up in that situation (it's having an issue building the closed generic type for activation).

Would it be possible for you to create a small sample using your types that you want to decorate and the decorators that are failing? You can remove the logic from the classes as I think it's probably some corner case of generics that I'm missing but I'm not able to create it using my simple test cases.

@ipjohnson ipjohnson added the bug label Feb 21, 2018
@davidkeaveny
Copy link
Author

davidkeaveny commented Apr 3, 2018

I've prepared a repro - the bug appears if there are multiple decorators registered, so if you comment out one or other of the decorator registrations the test passes.

I also modified the decorators so that they had no other dependencies (apart from the ICommandHandler<TCommand> obviously) and then the bug disappears, even with both decorators registered.

cqrs-decorator.zip

@ipjohnson
Copy link
Owner

Thanks @davidkeaveny that was very helpful. I was able to replicate the problem and fix it. I should be able to find some time this weekend to look at the other generic issue you raised but I'm not sure what/if there is a fix for it.

Either way I should be able to get a new version out for you this weekend. In the mean time if you want you can try the nightly build at https://ci.appveyor.com/nuget/grace-master

@ipjohnson
Copy link
Owner

I've released version 6.3.3 to address this issue and I'm marking it closed

@davidkeaveny
Copy link
Author

Looks great, @ipjohnson , thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants