Question

I am using simpleinjector 2.3.0.0 and .net 4.5.

I have been trying to register a Factory which has a dependency on a IEnumerable, and an instance of a Thing, plus a unitofwork decorator. (among other things)

Here is my factory:

public class ThingFactory : IThingFactory
{
    private readonly IEnumerable<IThing> things;

    public ThingFactory (IEnumerable<IThing> things)
    {
        this.things= things;
    }

    public IThing GetThing(ThingType thingType)
    {
     return things.FirstOrDefault(t => t.IsApplicable(thingType));
    }
}

and here is my implementation of IThing

 public class ThingOne: IThing
{
     private IThingFactory thingFactory;

    public ThingOne(IThingFactory thingFactory)
    {
        this.thingFactory= thingFactory;
    }

    public void Execute(MyDto myDto)
    {
       //do stuff
      //get the next approiate IThing out of the factory and execute
    }

    public bool IsApplicable(ThingType thingType)
    {
        return thingType == ThingType.ThingOne;
    }

}

my registration for this is

  container.RegisterAll<IThing>(
            from tp in typeof (IThing).Assembly.GetExportedTypes()
            where !tp.IsAbstract
            where typeof (IThing).IsAssignableFrom(tp)
            select tp);

   container.Register<IThingFactory , ThingFactory>();

This works absolutely fine and I can verify the container without exceptions. However when I also add the following registration to the container I get an exception

 container.RegisterDecorator(typeof(ICommandHandler<,>),    
 typeof(UnitOfWorkDecorator<,>));

The decorator is a Nhibernate UnitOFWork which manages an Nhibernate transaction(not that this matters)(which following this pattern https://cuttingedge.it/blogs/steven/pivot/entry.php?id=91)

  public class UnitOfWorkDecorator<TCommand, TResult>
    : ICommandHandler<TCommand, TResult> 
    where TCommand : ICommand
{

    public UnitOfWorkDecorator(
        ICommandHandler<TCommand, TResult> decoratedCommandHandler)
    {
        this.currentSessionContextService = currentSessionContextService;
        this.decoratedCommandHandler = decoratedCommandHandler;
    }

    public void Handle(TCommand command)
    {
          //start my nhibernate transaction
          decoratedCommandHandler.Handle(command);

          //commit the transaction                       
    }

So anyway, when I add the decorator, I get the following exception. when I call

  container.Verify();

Yet when I removed the decorator registration all is fine again.

Topshelf v3.1.107.0, .NET Framework v4.0.30319.18052
Topshelf.Hosts.ConsoleRunHost Error: 0 : An exception occurred, System.Reflectio
n.TargetInvocationException: Exception has been thrown by the target of an invoc
ation. ---> System.InvalidOperationException: The configuration is invalid. Crea
ting the instance for type IThingManager failed. The registered delegate for t
ype IThingManager threw an exception. The registered delegate for type IThingFa
ctory threw an exception. The registered delegate for type IEnumerable<IThing> th
rew an exception. No registration for type ThingOne could be found a
nd an implicit registration could not be made.  ---> SimpleInjector.ActivationEx
ception: The registered delegate for type IThingManager threw an exception. Th
e registered delegate for type IThingFactory threw an exception. The registered d
elegate for type IEnumerable<IThing> threw an exception. No registration for type
ThingOne could be found and an implicit registration could not be m
ade.  ---> SimpleInjector.ActivationException: The registered delegate for type
IThingFactory threw an exception. The registered delegate for type IEnumerable<IThing>     
threw an exception. No registration for type ThingOne could be
found and an implicit registration could not be made.  ---> SimpleInjector.Activ
ationException: The registered delegate for type IEnumerable<IThing> threw an exc
eption. No registration for type ThingOne could be found and an impl
icit registration could not be made.  ---> SimpleInjector.ActivationException: N
o registration for type ThingOne could be found and an implicit regi
stration could not be made.

Stacktrace:

at SimpleInjector.Container.ThrowNotConstructableException(Type concreteType)
at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type servic
eType)at SimpleInjector.Container.GetRegistration(Type serviceType, Boolean throwOn
 Failure)   at SimpleInjector.Advanced.ContainerControlledCollection`1.   
 <>c__DisplayClass1
0.<ToLazyInstanceProducer>b__f()   at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()     at     
SimpleInjector.Advanced.ContainerControlledCollection`1.<SimpleInjector.Ad
vanced.IContainerControlledCollection.GetRelationships>b__1(Lazy`1 p)
at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
at System.Linq.Enumerable.<SelectManyIterator>d__31`3.MoveNext()
at System.Linq.Enumerable.<DistinctIterator>d__81`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at SimpleInjector.Advanced.ContainerControlledCollection`1.SimpleInjector.Adv
anced.IContainerControlledCollection.GetRelationships()
at SimpleInjector.Extensions.Decorators.DecoratorHelpers.ContainerControlledC
ollectionRegistration.GetRelationshipsCore()
at SimpleInjector.Registration.GetRelationships()
at SimpleInjector.InstanceProducer.GetRelationships()
at SimpleInjector.Container.OnExpressionBuilt(ExpressionBuiltEventArgs e, Ins
tanceProducer instanceProducer)
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at SimpleInjector.InstanceProducer.BuildExpression()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.BuildExpression()
at SimpleInjector.Advanced.DefaultConstructorInjectionBehavior.BuildParameter
Expression(ParameterInfo parameter)
at SimpleInjector.Registration.BuildParameterExpressionFor(ParameterInfo para
meter)
at SimpleInjector.Registration.<BuildNewExpression>b__1a(<>f__AnonymousTypef`
2 <>h__TransparentIdentifier18)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at SimpleInjector.Registration.BuildNewExpression(Type serviceType, Type impl
ementationType)
at SimpleInjector.Registration.BuildTransientExpression[TService,TImplementat
ion]()
at SimpleInjector.Lifestyles.TransientLifestyle.TransientLifestyleRegistratio
n`2.BuildExpression()
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at SimpleInjector.InstanceProducer.BuildExpression()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.BuildExpression()
at SimpleInjector.Advanced.DefaultConstructorInjectionBehavior.BuildParameter
Expression(ParameterInfo parameter)
at SimpleInjector.Registration.BuildParameterExpressionFor(ParameterInfo para
meter)
at SimpleInjector.Registration.<BuildNewExpression>b__1a(<>f__AnonymousTypef`
2 <>h__TransparentIdentifier18)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at SimpleInjector.Registration.BuildNewExpression(Type serviceType, Type impl
ementationType)
at SimpleInjector.Registration.BuildTransientExpression[TService,TImplementat
ion]()
at SimpleInjector.Lifestyles.TransientLifestyle.TransientLifestyleRegistratio
n`2.BuildExpression()
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at SimpleInjector.InstanceProducer.BuildInstanceCreator(Object& createdInstan
ce)
at SimpleInjector.InstanceProducer.GetInstance()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.InstanceProducer.Verify()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.Verify()
at SimpleInjector.Container.VerifyProducers(InstanceProducer[] producersToVer
ify)
at SimpleInjector.Container.ValidateRegistrations()
at SimpleInjector.Container.Verify()

Thanks for your help

Edit - ThingManager is the entry point and looks like this:

   public class ThingManager : IThingManager
{
    private readonly IThingFactory thingFactory;

    public ThingManager(IThingFactory thingFactory)
    {
        this.thingFactory = thingFactory;
    }

    public void Run(MyDto myDto)
    {
        var firstThing = thingFactory.GetThing(ThingType.ThingOne);
        firstThing.Execute(myDto);
    }
}
Was it helpful?

Solution

The problem is caused by the circular reference in your code: ThingFactory depends on IEnumerable<IThing>, which depends on ThingOne, which depends on ThingFactory, which closes the loop.

Unfortunately due to a bug in Simple Injector 2.3 the real error describing the circular reference is hidden and a less expressive (and in this case even incorrect) message is thrown. This bug got fixed in Simple Injector 2.3.5.

Although the circular reference exists in your code base, the Verify method only notices this error when a decorator is registered. The internal ContainerControlledCollection<T> causes the registered elements to be precompiled which allows the InstanceProducer instances to notice the loop. Without the ContainerControlledCollection<T> the collection is evaluated more lazily which completely hides this error, even when resolving the type. This quirk in your design however might still cause all sorts of troubles later on.

For the coming Simple Injector 2.4 the ContainerControlledCollection<T> will actually act more lazily to improve performance in the case that Verify() isn't called (which makes sense for applications with deep object graphs and collections with many items), but this means that even with the decorator the verifier will not throw an exception.

The behavior of Simple Injector 2.4 not to throw an exception is actually correct (and the fact that 2.3 and 2.3.5 throw an exception can be considered a bug), since Simple Injector's goal is to prevent stackoverflow exceptions when building up object graphs (when GetInstance is called). Since the creation of types is delayed since an IEnumerable<T> is used (resolving an IEnumerable<T> does not resolve the instances; only iterating the collection does), there is no stackoverflow exception.

And although having the circular reference in your code might be a code smell, in your code it will probably only cause recursion in your code (but no stackoverflow) which is probably the intended behavior.

Still you might want to reconsider the design, since a recursion depth of 10 (say you have 10 things and call the factory to get the next thing in the list), this will trigger the creation of 55 things (10x the first element, 9x second element, 8x third, ... 1x the tenth), which might not be what you'd expect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top