simpleinjector / SimpleInjector

An easy, flexible, and fast Dependency Injection library that promotes best practice to steer developers towards the pit of success.
https://simpleinjector.org
MIT License
1.21k stars 153 forks source link

Using DependencyMetadata<T> inside a decorator applied to each individual type in a registered collection of types throws an exception during container verification #878

Closed RyanMarcotte closed 3 years ago

RyanMarcotte commented 3 years ago

To Reproduce

public class DecoratorWithDependencyMetadataAppliedToCollectionTests
{
    [Fact]
    public void SuccessfullyAppliesDecoratorWithDependencyMetadataToEachTypeInServiceCollection()
    {
        var container = new Container();
        container.Collection.Register<IDoTheThing>(new [] { typeof(Thing1), typeof(Thing2) });
        container.RegisterDecorator<IDoTheThing, Decorator>();

        container.Verify(); // throws exception
    }

    private interface IDoTheThing
    {
        void DoTheThing();
    }

    private class Thing1 : IDoTheThing
    {
        public void DoTheThing() { }
    }

    private class Thing2 : IDoTheThing
    {
        public void DoTheThing() { }
    }

    private class Decorator : IDoTheThing
    {
        private readonly IDoTheThing _thing;
        private readonly DependencyMetadata<IDoTheThing> _dependencyMetadata;

        public Decorator(IDoTheThing thing, DependencyMetadata<IDoTheThing> dependencyMetadata)
        {
            _thing = thing;
            _dependencyMetadata = dependencyMetadata;
        }

        public void DoTheThing() => _thing.DoTheThing();
    }
}

The above code is the minimum needed to reproduce the problem. In my own code, I have two decorators applied instead of just one. That is why I would like to use DependencyMetadata<T>: to retrieve information about the decorated implementation type (either Thing1 or Thing2 in the example above) for logging purposes.

Expected behavior

Per my understanding of the documentation here and here...

Actual behavior

System.InvalidOperationException: The configuration is invalid. Creating the instance for type DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing failed. No registration for type DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing could be found. There is, however, a registration for IEnumerable<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>; Did you mean to call GetAllInstances<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>() or depend on IEnumerable<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>? Please see https://simpleinjector.org/collections for more information about registering and resolving collections.

at SimpleInjector.InstanceProducer.VerifyExpressionBuilding()
at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt(InstanceProducer[] producersToVerify)
at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt()
at SimpleInjector.Container.VerifyInternal(Boolean suppressLifestyleMismatchVerification)
at SimpleInjector.Container.Verify(VerificationOption option)
at SimpleInjector.Container.Verify()
at (testClass).(methodName) in (file):line 123

SimpleInjector.ActivationException: No registration for type DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing could be found. There is, however, a registration for IEnumerable<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>; Did you mean to call GetAllInstances<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>() or depend on IEnumerable<DecoratorWithDependencyMetadataAppliedToCollectionTests.IDoTheThing>? Please see https://simpleinjector.org/collections for more information about registering and resolving collections.

at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type type)
at SimpleInjector.ProducerBuilders.DependencyMetadataInstanceProducerBuilder.BuildInstanceProducerForDependencyMetadata(Type metadataType)
at SimpleInjector.ProducerBuilders.DependencyMetadataInstanceProducerBuilder.TryBuild(Type serviceType)
at SimpleInjector.Container.BuildInstanceProducerForType(Type serviceType, Boolean autoCreateConcreteTypes)
at SimpleInjector.Container.<>c__DisplayClass192_0.<GetRegistrationEvenIfInvalid>g__BuildProducer|0()
at SimpleInjector.Container.GetInstanceProducerForType(Type serviceType, InjectionConsumerInfo consumer, Func`1 buildInstanceProducer)
at SimpleInjector.Container.GetRegistrationEvenIfInvalid(Type serviceType, InjectionConsumerInfo consumer, Boolean autoCreateConcreteTypes)
at SimpleInjector.Advanced.DefaultDependencyInjectionBehavior.GetInstanceProducer(InjectionConsumerInfo dependency, Boolean throwOnFailure)
at SimpleInjector.ContainerOptions.GetInstanceProducerFor(InjectionConsumerInfo consumer)
at SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)
at SimpleInjector.Registration.BuildNewExpression()
at SimpleInjector.Registration.BuildTransientExpression()
at SimpleInjector.Lifestyles.TransientLifestyle.TransientRegistration.BuildExpression()
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.ReplaceOriginalExpression(Registration decoratorRegistration)
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.ApplyDecorator(Type closedDecoratorType)
at SimpleInjector.Decorators.DecoratorInterceptor.TryToApplyDecorator(ExpressionBuiltEventArgs e)
at SimpleInjector.Decorators.DecoratorInterceptor.ExpressionBuilt(Object sender, ExpressionBuiltEventArgs e)
at SimpleInjector.Container.OnExpressionBuilt(InstanceProducer instanceProducer, Expression expression)
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at SimpleInjector.Internals.LazyEx`1.InitializeAndReturn()
at SimpleInjector.InstanceProducer.BuildExpression()
at SimpleInjector.InstanceProducer.VerifyExpressionBuilding()
dotnetjunkie commented 3 years ago

Thank you for this detailed report. Unfortunately, injecting DependencyMetadata<T> is not supported in decorators. For this another mechanism currently exists, which is DecoratorContext as explained here.

For consistency, however, it might be good to add DependencyMetadata<T> support for decorators as well.

dotnetjunkie commented 3 years ago

Moved to new feature request #879.

RyanMarcotte commented 3 years ago

I somehow missed that section of the documentation. Thank you for pointing me in the right direction!