autofac / Autofac

An addictive .NET IoC container
https://autofac.org
MIT License
4.44k stars 836 forks source link

Container-level configuration to throw exception on empty IEnumerable<T> resolves #1384

Closed jzabroski closed 1 year ago

jzabroski commented 1 year ago

Problem Statement

The documentation for Autofac is quite clear that when no services implement T, then IEnumerable<T> will return an empty set of dependencies. https://autofac.readthedocs.io/en/latest/resolve/relationships.html#:~:text=The%20enumerable%20support%20will%20return%20an%20empty%20set%20if%20no%20matching%20items%20are%20registered%20in%20the%20container.

Desired Solution

  1. ContainerBuilder new property: public bool ShouldThrowWhenResolveIEnumerableWouldBeEmpty { get; set; }
  2. container.Resolve<IEnumerable<T>> throws if there are no T that satisfy the IEnumerable. In this sense, the logic can do a look-through analysis to see that there are no T registered, without enumerating the IEnumerable.

Alternatives You've Considered

Alternative Number 1: Live With It

It's possible that Autofac's approach should just be "standard operating procedure" and I should defensively check in the constructor for empty collections.

In cross-referencing this with the current Microsoft.Extensions.DependencyInjection.Specification.Tests, there actually seems to be no well-defined behavior expected here across containers, which is shocking. (If I misread the code, please let me know - I am fairly new to studying the Specificaiton.Tests sln in the Microsoft repository and some of them are a bit terse and lacking explanation for what the "specification" is supposed to "test".) You can see the test fake here: https://github.com/dotnet/runtime/blob/8d3d7eaac5e4242ae30c4ad334b558d6cbd1e53a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs#L1164-L1169 but there is no explicit test for what to do when the resolution is empty.

Alternative Number 2: Resolve<ICollection<T>>

Another possibility is that Resolve<ICollection<T>> should exist, since, strictly speaking, knowing if anIEnumerable<T> is empty or not requires walking the enumerator and that could create side-effects.

That said, Autofac should also know if any types were found. It's not like it's desireable to late-bound bind the universe of types the IEnumerable would service. Effectively, it is like an ICollection even if it is a IEnumerable. - Unless there is some edge case I am not thinking about / have not seen.

Additional Context

tillig commented 1 year ago

I don't think we're going to do this. Generally speaking, most containers out there will resolve an empty enumerable if the service isn't registered. It's easy enough to verify that's also how the MS provider works by just setting up a unit test and checking, even if there's no spec test.

This has been the behavior of Autofac for years and there's not a lot of value in adding, owning, documenting, and supporting this flag given it's the expected behavior and is common amongst containers - not just Autofac.