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.22k stars 152 forks source link

Flowing Scoped Lifestyle and Scoped Collection resolve #948

Closed Glunder0 closed 2 years ago

Glunder0 commented 2 years ago

When DefaultScopedLifestyle is set to ScopedLifestyle.Flowing collection elements that are registered as Scoped can be only resolved from the first scope. Trying to resolve them in any consequent scope throws an exception.

var container = new Container();
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
container.Collection.Append<IInterface, Instance>(Lifestyle.Scoped);

using (var scope = new Scope(container))
{
    scope.GetInstance<IInterface[]>(); // Ok
}

using (var scope = new Scope(container))
{
    scope.GetInstance<IInterface[]>(); // Exception
}
Unhandled exception. SimpleInjector.ActivationException: Cannot access a disposed object.
Object name: 'SimpleInjector.Scope'.
 ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'SimpleInjector.Scope'.
   at SimpleInjector.Scope.ThrowObjectDisposedException()
   at SimpleInjector.Scope.GetInstanceInternal(ScopedRegistration registration)
   at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration registration, Scope scope)
   at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
   at lambda_method7(Closure )
   at SimpleInjector.InstanceProducer.GetInstance()
   --- End of inner exception stack trace ---
   at SimpleInjector.InstanceProducer.GetInstance()
   at SimpleInjector.Internals.ContainerControlledCollection`1.CopyTo(TService[] array, Int32 arrayIndex)
   at SimpleInjector.Internals.FlowingContainerControlledCollection`1.CopyTo(TService[] array, Int32 arrayIndex)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at SimpleInjector.InstanceProducer.GetInstance()
   at SimpleInjector.Container.GetInstance(Type serviceType)
   at SimpleInjector.Scope.GetInstance(Type serviceType)
   at SimpleInjector.Scope.GetInstance[TService]()

And there is a difference in resolving scoped collections and resolving scoped single instances with DefaultScopedLifestyle set to ScopedLifestyle.Flowing.

var container = new Container();
container.Options.DefaultScopedLifestyle = ScopedLifestyle.Flowing;
container.Register<IInterface, Instance>(Lifestyle.Scoped);
container.Collection.Append<IInterface, Instance>(Lifestyle.Scoped);

var scopeOne = new Scope(container);
var singleInstanceOne = scopeOne.GetInstance<IInterface>();
var collectionInstanceOne = scopeOne.GetInstance<IInterface[]>()[0];

var scopeTwo = new Scope(container);
var singleInstanceTwo = scopeTwo.GetInstance<IInterface>();
var collectionInstanceTwo = scopeTwo.GetInstance<IInterface[]>()[0];

Console.WriteLine(singleInstanceOne.Equals(singleInstanceTwo)); // False
Console.WriteLine(collectionInstanceOne.Equals(collectionInstanceTwo)); // True
dotnetjunkie commented 2 years ago

Two bugs for the price of one. Thanks for these very clear and reproducible bug reports.

I've got some bad news though, which is that I don't have a work around available and it might take some time for me to publish a new release that fixes these issues. ETA mid July.

dotnetjunkie commented 2 years ago

This bug has been fixed in v5.4. It can be downloaded from NuGet.