-
Notifications
You must be signed in to change notification settings - Fork 317
Changes to DependencyInjection requirements #433
Comments
From @dotnetjunkie
There is a lot of implicit behavior in the adapter that will have to be explicitly unspecified. In other words, the contract should explicitly state that in certain conditions an adapter is free to behave as it chooses. For instance, an adapter is allowed to throw an exception. When considering the current version of Simple Injector, the behavior as specified by the abstraction should be undetermined in the following scenarios:
Besides the above list of behavior that has to become explicitly unspecified, there are other behaviors of the abstraction that I’m currently unsure of how it should be specified for the current version of Simple Injector to conform. These included:
This is what I’ve been able to come up with at this point and that holds for the current version of Simple Injector. As already noted above, there is already a plan to change some behavior of Simple Injector in the next major version. I will likely have missed some things, so I will update this comment as I discover more items. |
From @slaneyrw @dotnetjunkie Looking at the perspective of a Unity user I echo most of your points, and explicitly call out the Cardinality problem. I have been a user of the Unity container for many years and i have a fork of the GitHub that I'm using to convert it to netstandard1.6. When i was trying to build an Adapter for IServiceCollection/IServiceProvider I immediately came across the issue of how to distinguish between a replacement registration and a multiple registration.
It become vastly more complicated with IServiceScope. It doesn't make sense to me have registrations for any type that is designed for a single instance when resolved to have both a Scope lifetime and singleton/transient lifetime. In Unity a Scoped lifetime would be implemented using the HierarchicalLifetimeManager. If a type was resolved from the composition root ( i.e. IServiceProvider ) or from the scope (IServiceScope ? ) then the same build plan would be used but the instances will be stored in their respective owner container. I would not be possible to store two separate registrations for two different implementation without creating a new registration in the scope container when it is created ( or black magic to "migrate" a parent registration marked as "Scope" ). Once again, having this distinction for a single instance resolve does not make sense to me. Without any cadinality metadata at registration I will be forced to maintain two sets of registrations. If Having to maintain a specific order for resolved However is there anything in the spec that indicates whether the order should be evaluated across the entire resolve set... i.e.
|
The problem with having cardinality defined up front for everything is that you lose the ability to distribute where registrations come from. There might be some default implementations in the box and the user might provide some. I'm not sure how that would work if you were forced to declare everything up front in a single call. I'm not even sure why that matters to be honest. We're currently going to change things so that an
We'd need to take a look at places where we do this and see if we can break that pattern. @slaneyrw about unity
Always preserve registration order (currently) using what. As I said before, we're looking at relaxing that requirement. You need to look at both the "composition root" and the "IServiceScope" depending on the lifetimes of the resolved services. |
That's crazy evil. Collection resolution is pretty important, especially when you have strategy pattern type services like something resembling an Optional services should still be able to be injected by either expanding the expression generator to mock up a default class for that service interface or by injecting |
Think again. Most containers disallow injecting |
Not a huge fan of optional constructor injection either, honestly. Just looking for the most easily cross container compatible answer without resorting to something like |
Almost finished a ServiceCollection adapter for Unity and came across a few nasties.
I think both of the aspects should be on the ServiceCollection interface so implementors can override the behaviour |
|
Yes, I am creating my own service provider, but NOT over the original ServiceDescriptors. The ServiceDescriptor is too simplistic for all but the basic mapping type to implementation. What about property injection, method invocation, interface and virtual method interception. These are all tools in the arsenal that are not supported using ServiceDescriptor. Asking the ServiceCollection for all the descriptors and giving it to a ServiceProvider feels wrong to me. It believe should be the responsibility of the ServiceCollection to initialise the ServiceProvider. So at the moment I have to return an IServiceProvider from the ConfigureServices method instead of relying on MVC to create it. I hope you don't remove that capability! Maybe the default MVC template should be changed to make it more explicit and return IServiceProvider from ConfigureServices. |
I don't know why that affects any of those features.
The service collection is just metadata for your actual container implementation. The service collection isn't your container registration API. So its the container implementors job to take the "metadata" and turn it into container specific API calls. The container implementor also needs to return an
That's how it works and will continue to work.
We landed on leaving it implicit for the default case. |
We've started on some of the proposed changes here:
/cc @tillig @alexmg @nblumhardt @seesharper @jeremydmiller @khellang |
I'm a bit torn on the explicit For example, Autofac intentionally returns an empty We couldn't even really add anything to the service provider to track which explicit Long story too long - I think it's cool to enforce the different registration mechanism as part of |
We have to, it's one of the things that ends up being hard to implement in some containers. We can't enforce |
I think it's fine if you don't rely on it as long as it's not also wrong if it isn't explicitly disallowed. |
@davidfowl Just thought about something... Does ctor injection go through the |
Uh, never mind. Just realized that it all depends on the call at the composition root and what the individual container does from the on. |
@tillig That's always a problem right? What should "the spec" recommend? If any framework relies on it then it won't work with whatever container doesn't support it. |
@davidfowl Yeah, that is the problem. Of course, if part of this is in an attempt to loosen the requirements, then saying "behavior is unspecified and shouldn't be counted on" is loosening the requirements and I'm for it. Adding the notion that "if a component isn't registered via the Just off the top of my head, that one would actually be harder to implement on Autofac with more breaking changes than the ordering requirement that was previously handled. So... "don't count on it because it's intentionally unspecified" == cool; "don't count on it and container must implement a behavior to disallow it" == not so cool. |
For what it's worth, makes me happy. Should be a lot easier to implement container adaptors. |
@slaneyrw sealed? No.
Explode. |
That's fine. Our spec tests won't require it to throw but our default container will throw. At the same time, we need a way to make sure that frameworks don't rely on things like this. It's fine for applications to rely on it though. |
/cc @dadhi @seesharper |
Some questions for the thread as we go further with the current plan:
|
I think I like the ability to have custom derived service descriptors as an extension mechanism, though admittedly I don't have a completely thought out use case right now. Maybe the ability to register decorator types? Just brainstorming. I think lifetimes should be mixable in By way of example, in Autofac if you register a singleton, request scope, and transient into the same container and resolve the enumerable from the request scope, you'll get all three. For example, day you have some message processor and you register handlers for messages. One handler may be expensive to instantiate or otherwise initialize but is stateless, so you can optimize by registering it as singleton. Another handler may be cheaper or stateful, so it's transient. When you resolve all the handlers you still want both. |
Just a note about EF. EF relies on collections of scoped services being constructor injected as Question about versioning: since these are very significant breaking changes shouldn't it bump the version of D.I. to 2.0? Otherwise NuGet is going to make it very easy for people to get combinations of packages that don't work together. |
What's the lifetime of the thing it's being injected into? Is that itself scoped? Would be good to look at some concrete examples.
This was one of the breaking changes we were going to take for 1.1 because of the impact on 3rd party containers.
Calling it 2.0 doesn't make it any harder for nuget to give you invalid combinations of packages. NuGet will roll you forward if any package depends on 2.0. |
Since I can't update this comment, here's a little update about Simple Injector and In Simple Injector Since other DI containers behave differently in this respect, the DI abstraction should make the exact behavior undetermined. |
+1 to closed generics overriding an open generic instance. |
/cc @muratg |
Did anything discussed above end up in the just released ASP.NET Core 1.1? |
I think (at least) the following was fixed in 1.1 (with #430):
|
Any news on removing the tracking of transient disposables outside of scopes? |
Is this still to be done? I cannot find any subclasses of ServiceDescriptor on the Dev branch |
This looks great. I would love to have improved integration when using SimpleInjector, preferrably such that SimpleInjector would be able to inject dependencies as action parameters (which is a brilliant feature). By the way, SimpleInjector 4 has just been released. Don't know if that helps. |
Just looking at the API for v2, looks like none of the service descriptor differentiation still hasn't surfaced Also new issues with bad constructor discovery
|
Realistically, we can't change anything here. The best bet for making things like unity, castle and other containers work would be to go to @dotnetjunkie approach outlined here aspnet/Mvc#5403. TL;DR if the container implementation can't conform then there's another approach that includes adapting various composition roots. I think this is the best path forward to making containers with completely incompatible semantics work. It does mean that both containers need to be bridged but that's basically how things used to work in the older days (pre-ASP.NET Core). |
Just had a look at @dotnetjunkie 's repo and I think there are major problems trying to run both side-by-side. I prefer that the container OWNS the composition root, and that is how I've got unity to work Using the I've solved the issues with the constructor selection by switching the constructor selector policy to match the default DI's behaviour, although I can't work out how it makes a distinction between LoggerFactory's 2 constructors - I think there is some undocumented behaviour in there. Cardinality hints ( or lack there of ) is still an issue, I was really hoping the v2 API was going to support this, but I can work around it. |
This issue was moved to dotnet/aspnetcore#2346 |
Moving the discussion from #416
The tricky part with supporting DI is that not all of the things can be codified into the interface definition. That would make it easier for sure but some of it is just impossible.
Right now we're researching a couple of things:
IOrdered<T>
)As for the a specific list of things we think can be candidates for potential removal:
The text was updated successfully, but these errors were encountered: