Skip to content

Commit 06e7be9

Browse files
committed
Bug fix: List<T> and T[] collections could only be resolved when the container was set up using the flowing scoped lifestyle. Fixes #948
1 parent 6ce06ce commit 06e7be9

File tree

2 files changed

+105
-5
lines changed

2 files changed

+105
-5
lines changed

src/SimpleInjector.Tests.Unit/FlowingScopeTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Collections.ObjectModel;
56
using System.Linq;
67
using Microsoft.VisualStudio.TestTools.UnitTesting;
78

@@ -293,6 +294,105 @@ public void GetInstanceScope_CalledWithDifferentScopes_ReturnsDifferentScopedIns
293294
Assert.AreNotSame(service1.Dependency, service2.Dependency);
294295
}
295296

297+
[TestMethod]
298+
public void ScopeGetInstance_ResolvingArrayOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance()
299+
{
300+
this.ScopeGetInstance_ResolvingCollectionOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance(
301+
scope => scope.GetInstance<ILogger[]>().Single());
302+
}
303+
304+
[TestMethod]
305+
public void ScopeGetInstance_ResolvingEnumerableOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance()
306+
{
307+
this.ScopeGetInstance_ResolvingCollectionOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance(
308+
scope => scope.GetInstance<IEnumerable<ILogger>>().Single());
309+
}
310+
311+
[TestMethod]
312+
public void ScopeGetInstance_ResolvingReadOnlyCollectionOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance()
313+
{
314+
this.ScopeGetInstance_ResolvingCollectionOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance(
315+
scope => scope.GetInstance<ReadOnlyCollection<ILogger>>().Single());
316+
}
317+
318+
private void ScopeGetInstance_ResolvingCollectionOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance(
319+
Func<Scope, ILogger> loggerResolver)
320+
{
321+
// Arrange
322+
var container = new Container();
323+
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
324+
container.Collection.Append<ILogger, NullLogger>(Lifestyle.Scoped);
325+
326+
container.Verify();
327+
328+
ILogger logger1, logger2;
329+
330+
// Act
331+
using (var scope = new Scope(container))
332+
{
333+
logger1 = loggerResolver(scope);
334+
}
335+
336+
using (var scope = new Scope(container))
337+
{
338+
logger2 = scope.GetInstance<ReadOnlyCollection<ILogger>>().Single();
339+
}
340+
341+
// Assert
342+
Assert.AreNotSame(logger1, logger2);
343+
}
344+
345+
[TestMethod]
346+
public void ScopeGetInstance_ResolvingTypeDependingOnArrayOnASecondScopeAfterTheFirstIsDisposed_ResultsInANewScopedInstance()
347+
{
348+
// Arrange
349+
var container = new Container();
350+
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
351+
container.Collection.Append<ILogger, NullLogger>(Lifestyle.Scoped);
352+
container.Register<ServiceDependingOn<ILogger[]>>();
353+
354+
ILogger logger1;
355+
ILogger logger2;
356+
357+
using (var scope = new Scope(container))
358+
{
359+
logger1 = scope.GetInstance<ServiceDependingOn<ILogger[]>>().Dependency.Single();
360+
}
361+
362+
using (var scope = new Scope(container))
363+
{
364+
logger2 = scope.GetInstance<ServiceDependingOn<ILogger[]>>().Dependency.Single();
365+
}
366+
367+
// Assert
368+
Assert.AreNotSame(logger1, logger2);
369+
}
370+
371+
[TestMethod]
372+
public void ScopeGetInstance_ScopeRegistrationsBothAvailableThroughCollectionsAsThroughOneToOneMappings_ResolveAsTheSameInstanceWithinASingleScope()
373+
{
374+
// Arrange
375+
var container = new Container();
376+
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
377+
container.Register<ILogger, NullLogger>(Lifestyle.Scoped);
378+
container.Collection.Append<ILogger, NullLogger>(Lifestyle.Scoped);
379+
380+
var scope1 = new Scope(container);
381+
var scope2 = new Scope(container);
382+
383+
// Act
384+
var singleInstance1 = scope1.GetInstance<ILogger>();
385+
var collectionInstance1 = scope1.GetInstance<ILogger[]>().Single();
386+
387+
var singleInstance2 = scope2.GetInstance<ILogger>();
388+
var collectionInstance2 = scope2.GetInstance<ILogger[]>().Single();
389+
390+
// Assert
391+
Assert.AreSame(singleInstance1, collectionInstance1);
392+
Assert.AreNotSame(singleInstance1, singleInstance2);
393+
Assert.AreSame(singleInstance2, collectionInstance2);
394+
}
395+
296396
public class ScopedPluginProxy : IPlugin
297397
{
298398
public readonly Func<Scope, IPlugin> Factory;

src/SimpleInjector/ProducerBuilders/CollectionInstanceProducerBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ internal sealed class CollectionInstanceProducerBuilder : IInstanceProducerBuild
2222
private static readonly MethodInfo EnumerableToListMethod =
2323
typeof(Enumerable).GetMethod(nameof(Enumerable.ToList));
2424

25-
private readonly Dictionary<Type, InstanceProducer?> emptyAndRedirectedCollectionRegistrationCache =
26-
new Dictionary<Type, InstanceProducer?>();
25+
private readonly Dictionary<Type, InstanceProducer?> emptyAndRedirectedCollectionRegistrationCache = new();
2726

2827
private readonly Container container;
2928

@@ -98,9 +97,10 @@ private InstanceProducer BuildMutableCollectionProducerFromControlledCollection(
9897
private Expression BuildMutableCollectionExpressionFromControlledCollection(
9998
Type serviceType, Type elementType)
10099
{
101-
var streamExpression = Expression.Constant(
102-
value: this.container.GetAllInstances(elementType),
103-
type: typeof(IEnumerable<>).MakeGenericType(elementType));
100+
InstanceProducer streamRegistration =
101+
this.container.GetRegistration(typeof(IEnumerable<>).MakeGenericType(elementType))!;
102+
103+
Expression streamExpression = streamRegistration.BuildExpression();
104104

105105
if (serviceType.IsArray)
106106
{

0 commit comments

Comments
 (0)