Skip to content

Commit daf4cfd

Browse files
committed
Decorators in flowing scope could cause stackoverflow exception. Fixes #998
1 parent 59c7205 commit daf4cfd

File tree

2 files changed

+30
-4
lines changed

2 files changed

+30
-4
lines changed

src/SimpleInjector.Tests.Unit/FlowingScopeTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,19 @@ public void Scoped_instances_resolved_from_different_scopes_within_the_context_o
411411
Assert.AreNotSame(instance.Service1, instance.Service2);
412412
}
413413

414+
// #998
415+
[TestMethod]
416+
public void Verify_OnCollectionComponent_InjectedWithATransientDecorator_Succeeds()
417+
{
418+
var container = new Container();
419+
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
420+
container.Register<IPlugin, PluginImpl>();
421+
container.RegisterDecorator<IPlugin, PluginDecorator>();
422+
container.Collection.Register<IX>(typeof(XDependingOn<IPlugin>));
423+
424+
container.Verify();
425+
}
426+
414427
public class ResolvingFromScopesInCtor<TService> where TService : class
415428
{
416429
public TService Service1 { get; }
@@ -444,5 +457,10 @@ public ScopedCommandHandlerProxy(Func<Scope, ICommandHandler<T>> decorateeFactor
444457
this.DecorateeFactory = decorateeFactory;
445458
}
446459
}
460+
461+
public sealed class PluginDecorator : IPlugin
462+
{
463+
public PluginDecorator(IPlugin plugin) { }
464+
}
447465
}
448466
}

src/SimpleInjector/InstanceProducer.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,20 @@ private static bool ContainsScopedComponentsInGraph(InstanceProducer producer)
536536
producer.BuildExpression();
537537
}
538538

539-
return
540-
producer.Lifestyle is SingletonLifestyle ? false :
541-
producer.Lifestyle is ScopedLifestyle ? true :
542-
producer.GetRelationships().Any(r => ContainsScopedComponentsInGraph(r.Dependency));
539+
return ContainsScopedComponentsInGraphRecursive(producer);
543540
}
544541

542+
// We suppress building expressions for dependencies, because in the case of a decorator, the list of
543+
// relationships will contain the decoratee, which will have its IsExpressionCreated set to false,
544+
// even after BuildExpression() is called on the decorator. Calling BuildExpression on the decoratee
545+
// will cause its producer to change and the decorator to be applied... again. This would cause the
546+
// generation of an endless stream of new decorators and will lead to a stackoverflow exception.
547+
// This is why BuildExpression is not called here.
548+
private static bool ContainsScopedComponentsInGraphRecursive(InstanceProducer producer) =>
549+
producer.Lifestyle is SingletonLifestyle ? false :
550+
producer.Lifestyle is ScopedLifestyle ? true :
551+
producer.GetRelationships().Any(r => ContainsScopedComponentsInGraphRecursive(r.Dependency));
552+
545553
private Action<Scope>[] GetVerifiers()
546554
{
547555
lock (this.locker)

0 commit comments

Comments
 (0)