Answered by:
Feedback on ASP.NET vNext Dependency Injection

Question
-
User1807617904 posted
It looks like one of the proposed new features of the next version of ASP.NET is a library or API simply titled Dependency Injection. In this post, I will provide feedback to the team on that particular sub-project, in the form of an open blog post. The contents of this post was originally posted on my blog.
Dependency Injection support
The details on the motivation for the Dependency Injection library are sparse, but I assume that the purpose is provide 'Dependency Injection support' to ASP.NET. If so, that motivation is laudable, because Dependency Injection (DI) is the proper way to write loosely coupled code when using Object-Oriented Design.
Some parts of the ASP.NET family already have DI support; personally, I'm most familiar with ASP.NET MVC and ASP.NET Web API. Other parts have proven rather hostile towards DI - most notably ASP.NET Web Forms. The problem with Web Forms is that Constructor Injection is impossible, because the Web Forms framework doesn't provide a hook for creating new Page objects.
My interpretation
As far as I can tell, the current ASP.NET Dependency Injection code defines an interface for creating objects:
public interface ITypeActivator { object CreateInstance( IServiceProvider services, Type instanceType, params object[] parameters); }
In addition to this central interface, there are other interfaces that enable you to configure a 'service collection', and then there are Adapters for
- Autofac
- Ninject
- StructureMap
- Unity
- Caste Windsor
As far as I can tell, there's no web code in the ASP.NET Dependency Injection code base. In other words, this is a poster example of a Conforming Container.
My recommendations
It's an excellent idea to add 'Dependency Injection support' to ASP.NET, for the few places where it's not already present. However, as I've previously explained, a Conforming Container isn't the right solution. The right solution is to put the necessary extensibility points into the framework:
- ASP.NET MVC already has a good extensibility point in the IControllerFactory interface. I recommend keeping this interface, and other interfaces in MVC that play a similar role.
- ASP.NET Web API already has a good extensibility point in the IHttpControllerActivator interface. I recommend keeping this interface, and other interfaces in Web API that play a similar role.
- ASP.NET Web Forms have no extensibility point that enables you to create custom Page objects. I recommend adding an IPageFactory interface, as described in my article about DI-Friendly frameworks. Other object types related to Web Forms, such as Object Data Sources, suffer from the same shortcoming, and should have similar factory interfaces.
- There may be other parts of ASP.NET with which I'm not particularly familiar (SignalR), but they should all follow the same pattern of defining Abstract Factories for user classes, in the cases where these don't already exist.
In addition to adding these required extensibility points, I recommend completely abandoning the project of defining a Conforming Container. The extensibility points should be added where they're used - the MVC Factories as part of MVC, the Web Form Factories as part of Web Forms, etc. This will have the added benefit of making the ASP.NET Dependency Injection project redundant. Less code is better than more code.
"perfection is attained not when there is nothing more to add, but when there is nothing more to remove." - Antoine de Saint Exupéry
These are my general recommendations to the team, but if desired, I'd also like to offer my assistance with any particular issues the team might encounter.
Monday, May 26, 2014 4:28 PM
Answers
-
User-260435932 posted
We have a few other scenarios that lead to this design:
- The ability to chain containers - As you walk up the execution stack there are different layers of dependency resolvers. The runtime has services, the web hosting has services, the webserver has services, and these should flow freely throughout the system. One example of this is the fact that MVC uses a service from the core host (KRuntime) to reason about the appliction's dependency graph to find controllers. Everyone can see everybody else's services (which leads me to my next point).
- Services can flow across frameworks - This is one thing I've wanted since we did SignalR. Today there's no way to get SignalR's broadcaster into a MVC or WebAPI action. This system makes that just work by default. AddSignalR, AddMvc and boom, services are freely flowing between the two systems. Now your application can publish from middleware, a controller action and arbitrary class activated with TypeActivator and the power of DI starts to show itself.
- DI required by frameworks - Our frameworks all require dependency injection now, it's not an after thought nor do we have our own set of services. This has the same benefits as the previous bullet. Services can flow out of the framework they are defined in and can be composed.
- Cross cutting concerns can be customized across the board - You can replace logging for the entire application via a single entry point.
That's a brain dump (probably not complete) of some of the things that lead to the current approach.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Saturday, May 31, 2014 5:43 AM
All replies
-
User156502145 posted
Also, take a look at how FubuMVC has implemented IoC. It is the most frictionless and enjoyable experience I've had so far. Nested containers (In StructureMap parlance) scoped to a request should also be a first class citizen, not something that has to be jerry rigged (Like in the current incarnation of MVC). Being able to scope things like connections and transactions to a single request has a lot of utility.
Tuesday, May 27, 2014 10:38 AM -
User1807617904 posted
As a follow-up, I would like to point to a short Twitter interchange I had with David Fowler and Gary McAllister, the essence of which seems to be
"Seen the feedback but there's other goals not taken into account."
I would love to hear about those other goals, so that I can help you avoid the most common pitfalls of 'DI-enabling' a framework.
Thursday, May 29, 2014 8:46 AM -
User-1235563788 posted
First, I am in agreement with everything Ploeh has said so far and I would throw in another point that I didn't see explicitly in his posts.
The top-most goal is to make a framework for developers to consume. Overgeneralizing this structure not only offers the potential for leading developers into bad practices, it also hinders their ability to adopt it. By making the construct this general, it doesn't communicate to the user any more. A search for ITypeActivator is going to have far less relevant results than a search for IXyzControllerFactory, it's going to be more obvious what to expect when you see an instance of IXyzControllerFactory defined, and IXyzControllerFactory in a class definition gives you information that you would otherwise have to scan through a long file (containing all the objects) to obtain.
IXyzControllerFactory is more restrictive. It has a specific purpose and it speaks directly to that purpose alone. This is not a bad thing. Unnecessary generalization introduces all sorts of cases that the framework isn't expecting and I guarantee there are going to be projects out there misuing the ITypeActivator interface for their own uses (and then getting stuck in two versions when you pull it out or need to change it).
The consumer for this framework is people, not computers. Overly broad generalizations may cut some hours off the development of the framework, but the consumers (people) are going to each lose at least as much time as you gain by going this route.
Now that Ploeh has brought this to my attention, I'm going to have to go see how much non-value added work this is going to add to my team's plate when it is released. We have a profitable, long term product and we refuse to choose the "just stop upgrading and take the long term support plan" path, so every time there is a breaking change we have to invest in rework. It's unfortunate so many of these changes seem aimed at green field projects.
I'm interested in the additional goals as well, as perhaps we can find an alternative route with fewer or more palatable sacrifices.
Friday, May 30, 2014 7:01 AM -
User-260435932 posted
We have a few other scenarios that lead to this design:
- The ability to chain containers - As you walk up the execution stack there are different layers of dependency resolvers. The runtime has services, the web hosting has services, the webserver has services, and these should flow freely throughout the system. One example of this is the fact that MVC uses a service from the core host (KRuntime) to reason about the appliction's dependency graph to find controllers. Everyone can see everybody else's services (which leads me to my next point).
- Services can flow across frameworks - This is one thing I've wanted since we did SignalR. Today there's no way to get SignalR's broadcaster into a MVC or WebAPI action. This system makes that just work by default. AddSignalR, AddMvc and boom, services are freely flowing between the two systems. Now your application can publish from middleware, a controller action and arbitrary class activated with TypeActivator and the power of DI starts to show itself.
- DI required by frameworks - Our frameworks all require dependency injection now, it's not an after thought nor do we have our own set of services. This has the same benefits as the previous bullet. Services can flow out of the framework they are defined in and can be composed.
- Cross cutting concerns can be customized across the board - You can replace logging for the entire application via a single entry point.
That's a brain dump (probably not complete) of some of the things that lead to the current approach.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Saturday, May 31, 2014 5:43 AM -
User-1908504355 posted
ITypeActivator isn't a central interface which is required of conforming containers. Rather, it's the code that we found to was common between the various places where a class was being instantiated wihch wasn't itself registered as a service. In other words - it's not implemented by the container or a constraint of the container, it's a helper where you would have cut and paste the same reflection code that call.
We will also very likely have IControllerActivator and IViewActivator to override those cases specifically. In those cases the default implementation will likely depend on ITypeActivator.
The approach we took was not to bring all containers down to a lowest common denominator, but we did need to establish what the least set of container behavior our components were limited to assuming were present. By limiting out components to this set of behaviors they can be registered and function properly when registered into any application's IoC container if they bring one to the table. However, because the application itself knows what container it is using, it's own startup code and components are free to use the full api and feature set of the container in question.
That is nothing new of course. It's just how you make IoC friendly components.
The place we did define a common representation of an IoC concept, in order for our frameworks to be more easily added to arbitraty containers, was IServiceDefinition. Our frameworks are able to provide the list of default services they are based on when asked, and it's done in a way that's meant to be looped through and registered into your container through the container-specific apis. That is also common code, so when you see things like support for Windsor it's that code the application would need to write that loops though a set of IServiceDefinition and registers them in that container.
That said - the application itself does not need to use define it's own components through IServiceDefinition. Instead I'm certain you'd use the container's own api to add all of the application's components and services.
The tricky part is finding the balance around what set of behaviors are safe to assume can be provided by the containers people will want to use around our components. So far those are something along these lines
* constructor injection of service interfaces
* service IEnumerable<T> is a way to have multiple, or optional, service T
* open generic services and implementations will be closed when needed typeof(IHello<>) typeof(Hello<>) will give you Hello<string> when you resolve IHello<string>
* in the unfortunate case that a service must be resolved in a lazy fashion, the existing System.IServiceProvider is constructor injected. the container does not need to implement this interface, but a component that implements IServiceProvider must be registered, which returns a calls to the container's native api to resolve
* there is a way to create and dispose a child scope for resolving components, this is used most obviously in the place where the web stack creates a per-request container
* chaining - this is the most complex one actually. because the "hosting" offers it's default capabilities as an IServiceProvider to the application, it's assumed that the application created IoC container can be created in a way where an unknown service will fallback as a call to the host's IServiceProvider. Because this chaining is tricky to set up correctly, that's also something we put in the code that registers the IServiceDescriptor array into a specific container.
It's also assumed that when an IoC container has it's own additional library to do this setup, then we won't need to provide a ms library with that code. That would be similar to how most containers have some sort of additional library with code specific to using it with web api, mvc, etc.
Saturday, May 31, 2014 2:23 PM -
User1807617904 posted
David and Louis, thank you for responding.
As far as I can tell, there are several forces at play, but it may be sensible to divide them into two categories - at least, this is how it currently sounds to me:
- Better DI options for framework users (i.e. programmers using the various ASP.NET frameworks to build web sites/services)
- Better integration of the various ASP.NET sub-frameworks (MVC, Web API, Web Forms, etc.)
The first goal, it seems to me, is aimed at Microsoft's customers; the second, at Microsoft's developers. In my experience, when you have two different concerns, it would be wise to keep them separate. From what you are writing, it sounds to me like these two concerns are currently being conflated, and I must admit that this makes me uneasy.
Both of you paint with rather broad strokes, so it's difficult for me to see exactly how your proposed design address the problems you list. Can you perhaps share some more details on how the particular design helps to address a particular problem? Out of self-interest, I really would like to help, because in my experience, once you start modeling things around the Service Locator anti-pattern, there's always a better, cleaner, simpler alternative available. Sometimes, it takes experience to identify those alternatives, and that's why I'm offering my help: more brains are better at finding a good solution than fewer brains :)
Here's an example of a question I got the other day: what about a project with both Web.Api and MVC controllers? Where is the composition root to handle both?
That's easy to do. You just create a Composition Root that implements both required interfaces:
public class SomeCompositionRoot : IHtppControllerActivator, IControllerFactory { // Singleton-scoped services are declared here... private readonly SomeThreadSafeService singleton; public SomeCompositionRoot() { // ... and (Singleton-scoped services) are initialised here. this.singleton = new SomeThreadSafeService(); } // Implement IControllerFactory here to support MVC // Implement IHtppControllerActivator here to support Web API }
In this way, any services a (programmer) user of ASP.NET needs to share between MVC and Web API can simply be declared as a class field of the SomeCompositionRoot class. Then you create a single instance of the SomeCompositionRoot class, and register it with both MVC and Web API.
From the viewpoint of a user of the ASP.NET frameworks, nothing more is required to flow services between frameworks. The necessary extensibility points are already in place.
From your answers, I understand that you Microsoft programmers also feel a need to be able to share services, code bases etc. across various sub-frameworks, and that sounds perfectly sensible, but is there any reason you can't just use Pure DI? Why do you need to introduce a Container Abstraction?
Dependency Injection is a set of principles (mainly the Dependency Inversion Principle) and patterns, and is best applied without using a DI Container.
Is there any particular reason you decided that something like this
public interface IServiceLocator { T Create<T>(object context); }
is better than something like this?
public interface IFactory<T> { T Create(object context); }
Can you share one or more concrete examples of how an Abstract Registration API solves a particular problem? Again, I would be very interested in seeing this, because (out of self-interest), I would like to propose a better alternative if I can identify one.
Thursday, June 5, 2014 2:14 AM -
User-1772813213 posted
That is nothing new of course. It's just how you make IoC friendly components.This is one way to make components IoC compatible. The most friendly way, as already mentioned by Ploeh several times, is not to use a container abstraction at all, instead use appropriate abstract factories. In fact, abstract factory will solve all the issues mentioned and in a much more elegant and extensible fashion.
Thursday, June 5, 2014 4:31 AM -
User1807617904 posted
FWIW, in my original article about DI-friendly frameworks, I didn't provide an example of how to define DI-friendly attributes, although I talked about them.
In order to address that shortcoming, I've now added a new article with an example of how to define DI-friendly attributes.
Friday, June 13, 2014 6:08 AM -
User156502145 posted
Action filters, especially filter attrributes, are a huge fail IMO. I remember in the early days wiring up DI for filter attributes and thinking how asisinine it was (And was ultimitely one of the reasons we ditched ASP MVC: http://www.mikeobrien.net/blog/on-filters-in-mvc3/). I really wish the ASP.NET vNext team would opt for FubuMVC's behavior chains instead of action filters. They are super DI friendly and a much better approach IMO. Jeremy Miller has a nice overview here:
I really hope vNext drops these high friction and DI unfriendly constructs. You can add/improve DI integration points all you want but if you retain these obtuse constructs it's not really a net improvement IMO.
Friday, June 13, 2014 10:18 AM -
User1807617904 posted
Mike, this reminds me of OWIN. Would it be fair to say that if ASP.NET vNext goes all in on OWIN, there would be no need for any other sort of filter?
Friday, June 13, 2014 2:24 PM -
User156502145 posted
I would think so as action filters, behavior chains, ect also deal with higher level concerns like transaction scope, logging, exception handling, etc.
The behavior chain aka "russian doll model" approach is a very elegant way to handle these cross cutting concerns (and DI friendly). Here is an example of our transaction scope behavior for fubu:
public class OverrideTransactionScopeAttribute : Attribute { } public class TransactionScopeBehavior : IActionBehavior { private readonly IActionBehavior _innerBehavior; private readonly IUnitOfWork _unitOfWork; public TransactionScopeBehavior(IActionBehavior innerBehavior, IUnitOfWork unitOfWork) { _innerBehavior = innerBehavior; _unitOfWork = unitOfWork; } public void Invoke() { using (var transaction = _unitOfWork.Transaction.Begin()) { try { _innerBehavior.Invoke(); transaction.Commit(); } catch { transaction.Rollback(); throw; } } } }
And its configuration:
public class Conventions : Infrastructure.Application.Web.Conventions { public Conventions() { Policies ... .WrapBehaviorChainsWith<AuthenticationBehavior>(x => x.IsSecure()) .WrapBehaviorChainsWith<TransactionScopeBehavior>(x => !x.HasAttribute<OverrideTransactionScopeAttribute>()) .WrapBehaviorChainsWith<CacheBusterBehavior>(x => !x.HasAttribute<AllowCachingAttribute>()) .WrapBehaviorChainsWith<SslRequiredBehavior>(x => x.IsSecure()) .WrapBehaviorChainsWith<AjaxExceptionHandlerBehavior>(); } }
So the behavior is pretty much a poco and the attributes are just a flag for the convention you specify (Not the actuall code doing the work). The dependencies, scoped to the request, are injected into the behavior as is the inner behavior. This allows you to do nifty things like wrapping inner behaviors with try/catches and using blocks.
Anyways, when we went from struggling to get IoC implemented in MVC to fubu where it was just baked in and all the constructs where DI friendly it made a huge difference. Hoping vNext will move this direction.
Friday, June 13, 2014 2:54 PM -
User1807617904 posted
FWIW, the 'russian doll model' is also know as the Decorator pattern, which is a very DI-friendly pattern.
Friday, June 13, 2014 3:00 PM -
User647039985 posted
Why couldn't use the Unity to handle de DI?
Wednesday, June 18, 2014 9:06 AM -
User1331021659 posted
Mark,
Having read the original article, I think you're missing a lot of important points. The vNext DI model seems to be intended to be a pervasive DI model that works not just with the internal workings of the framework, but also libraries and services you provide to the project. Controller Factories are *NOT* good enough. Adding Page Factory is a good idea, but not for DI purposes.
One of the big problems with DI right now is there isn't an easy (or good) way to develop a third party library that uses DI, because your composition root generally is in the application itself, and you don't really want to impose a requirement to use a specific container because someone using your library may already be using a different container.
All too many people look at DI as an application domain problem, but it's not. It's a system domain problem (ie, it involves all components in the system).
DI goes beyond instancing controllers. What about all the other parts of the system that can benefit from DI but do not get composed from the controller? Model Binders, Attributes of various kinds, Filters (which may or may not be an attribute), AOP based code (ie interception), Value Providers, Membership and Identity, etc... Are you going to make us override 200 different extension points with their own ModelBinderFactory, FilterFactory, etc.. for every possible extension point when we add DI to a system?
The fact is, I don't *WANT* to write custom factories. I want my DI to just plug into the framework, and i'd like my libraries to be able to plug into it, and i'd like different layers to be able to take advantage of it. Custom factories are called "custom" for a reason, and should be left to the application domain need rather than taken over by something as integral to the system as DI. If the framework is written to depend on DI from the get go, there is no reason NOT to bake DI support directly in, rather than leaving it as an "extension point" bolt on, that uses extension points that others may want to use for different reasons.
This isn't even getting into the issue of where you have competing third party components that replace your controller factories. Now you have to choose between factories, or write some kind of aggregation factory that integrates them both. Simply put, I don't believe that something that should be as integral to a system as DI should be left to the end user to figure out how to integrate.
Having said that, i'm not entirely happy with the solution proposed by the team either. I'm not convinced that the "Per Request" model proposed is sufficient. And i'm concerned that it may cause problems with existing containers.
Monday, June 23, 2014 12:09 PM -
User821835506 posted
The ability to chain containers - As you walk up the execution stack there are different layers of dependency resolvers. The runtime has services, the web hosting has services, the webserver has services, and these should flow freely throughout the system. One example of this is the fact that MVC uses a service from the core host (KRuntime) to reason about the appliction's dependency graph to find controllers. Everyone can see everybody else's servicesCouldn't this same goal be achieved using a composition root above the individual application level (as would be possible with OWIN) combined with the abstract factory dependency resolution strategy advocated by Ploeh?
One of the big problems with DI right now is there isn't an easy (or good) way to develop a third party library that uses DI, because your composition root generally is in the application itself, and you don't really want to impose a requirement to use a specific container because someone using your library may already be using a different container.[...]
DI goes beyond instancing controllers. What about all the other parts of the system that can benefit from DI but do not get composed from the controller? Model Binders, Attributes of various kinds, Filters (which may or may not be an attribute), AOP based code (ie interception), Value Providers, Membership and Identity, etc... Are you going to make us override 200 different extension points with their own ModelBinderFactory, FilterFactory, etc.. for every possible extension point when we add DI to a system?
I had some of these same thoughts at first but reading the DI Friendly Framework article Ploeh linked earlier in the thread has gone a long way toward convincing me otherwise. The use of abstract factories combined with a configuration class (which can provide default service implementations) seems like a good way to maintain a completely flexible design without imposing onerous coding / configuration / wiring duties on the user.
Wednesday, June 25, 2014 12:37 AM -
User1807617904 posted
Mystere, reading your comment, I get the impression that you seem to think that DI requires a DI Container. My entire feedback is that if you model 'DI support' with implicit assumption that a DI Container is a required component of doing DI, you'll arrive at a horrible mess.
Dependency Injection is a set of principles (the Dependency Inversion Principle) and patterns (Constructor Injection, Composition Root, Decorator, Composite, Abstract Factory, etc.). It's best done without a DI Container, but you can add a DI Container to the mix if you have special requirements.
A pervasive DI model is a model that follows these principles and patterns; it's not a model that attempts to force a Conforming Container into the code base.
"One of the big problems with DI right now is there isn't an easy (or good) way to develop a third party library that uses DI"
What makes you think that? It's perfectly possible to write a DI friendly library for ASP.NET. One example is Hyprlinkr: the entire code base of Hyprlinkr is based on the DI principle and patterns, and there's absolutely no DI Container involved in the Hyprlinkr code base. Yet, if you wish to wire Hyprlinkr up with a DI Container, you can easily do that. The reason is that, instead of attempting to force a Conforming Container upon you, the library is simply DI friendly.
"All too many people look at DI as an application domain problem, but it's not. It's a system domain problem"
It's both; it's just the proper way to do object-oriented design.
"Are you going to make us override 200 different extension points"
Hardly. You listed Controllers, Filters, Value Providers... That would be three different extensibility points for a framework. The rest of the services you mention are just that: Services. You can compose these from within your Controllers, Filters, etc. if you need to. In reality, there's probably going to be more then three extensibility points for a framework, but hardly 200; it's going to be a (small), finite number of well-known extensibility points.
"The fact is, I don't *WANT* to write custom factories."
And you don't have to, either. If you want to use the DI Container of your choice, you can. Here's a completely reusable implementation of ASP.NET MVC's IControllerFactory, using Castle Windsor:
public class WindsorControllerFactory : DefaultControllerFactory { private readonly WindsorContainer container; public WindsorControllerFactory(WindsorContainer container) { this.container = container; } protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType) { return (IController)this.container.Resolve(controllerType); } public override void ReleaseController(IController controller) { this.container.Release(controller); } }
You can create similar implementation for the other, well-known extensibility point of the framework, but you don't even have to do this yourself. Your DI Container vendor can do this as a pre-packaged Glue Library and publish it on NuGet, because these implementations are universally reusable.
My point is that a Conforming Container isn't required here; in fact, it's completely redundant, so only amounts to overdesign and needless complexity.
So, I don't believe that I'm "missing a lot of important points". Quite the contrary, I'm just pointing out that there are much simpler solutions available to address all of these concerns, rather than the currently proposed architecture.
Wednesday, June 25, 2014 2:18 AM -
User916175356 posted
I would be very curious to hear more from the asp.net team on why we can't simply go the abstract factory route and we're leaning towards the service locator / conforming container again? I can partially see the appeal of making stuff look easy for the users (i.e. they don't have to implement 5 interfaces, just one), but I sincerely believe that a few bits of written guidance would take care of this, with the added benefit of teaching people good design (ploeh's blog posts are a great example of how such docs might look like). And in the end, if we really really want to have one interface, why not simply leave other extensibility points in place, for the cases when they'd really be needed?
Wednesday, July 2, 2014 5:31 AM -
User1331021659 posted
Mark, I'm fully aware that DI does not require a container. And I'm fully aware of the definitions. You seem to have missed the point. In several places. Again.
You've made it clear that your primary concern regarding DI ends at the class design. And from a purist point of view, that makes sense. From a library or framework authors point of view, they must also take into account the details of configuration. It's a very poor library that throws configuration totally over the wall to the library end user and says "You deal with it", particularly when it involves internal implementation objects that end user doesn't see or know anything about.
You clearly understand this problem, because you try to solve it with factories and facades, but that only solves part of the problem and doesn't deal with lifetime management. Particularly when you get into issues of per request lifetimes, or singleton lifetimes. It also makes the library author do a lot of extra work. Work that a container already does quite nicely.
To this point, when I design a library, I would like to not only make it DI Friendly, but I want to take advantage of the benefits of using a container. And I don't see anything wrong with that. I Also don't want to force my container choice on my library user. So what are my options? Certainly none that you have presented so far.
The point being, your arguments against a conforming container expectation in a framework or library ignores that the framework or library should be able to take advantage of conforming containers benefits if the author wants to. Yes, the classes should be designed to pure DI standards, but the integration of the library into the application is the real problem that needs to be solved, and not just with factories or facades.
Not all libraries are simple utility classes that can be easily wired up in the applications composition root. Many are complex systems in and of themselves. Take for instance, a PDF rendering library that might have an internal set of dozens or even hundreds of dependencies that the library user never sees (or only sees through properties).
Regarding extensibility points. Whether there's 3 or 30 doesn't really matter. Extensibility points are there for end users to extend the framework, but you're now taking over that extensibility with a basic function. When I said I don't want to be creating custom factories, it is irrelevant that that custom factory might come from someone else. It's the *USE* of that factory that's the issue. I now have to choose how I want to use that extension point if I have two competing concerns that need it.
What if I want to add a DI container to an existing application, and I find that someone has already written a custom controller factory? Or maybe they're using a third party library that implements a custom controller factory already? My point here is that you're *STEALING* extensibility from the application author by insisting these extensibility points be used for such common base functionality.
So no, I'm not assuming that DI requires a container. But framework and library authors may want the benefits of a container, without forcing undue configuration on the library user.
Wednesday, July 2, 2014 5:38 AM -
User1331021659 posted
I had some of these same thoughts at first but reading the DI Friendly Framework article Ploeh linked earlier in the thread has gone a long way toward convincing me otherwise. The use of abstract factories combined with a configuration class (which can provide default service implementations) seems like a good way to maintain a completely flexible design without imposing onerous coding / configuration / wiring duties on the user.Mark's solution is to "steal" framework extensibility from the application developer, by using the extension methods for something that should be baked directly into the framework. If I have a need to implement a customer controller factory, for instance, I don't want to be in the situation where my DI framework is already using that extensibility point. And, as I argued in my previous point, this ignores all the other extensibility points which mark suggests the end user should also have to take over.
What's more, the framework now can't take advantage of a container, and must be written to do all the work a container provides. Mark seems to be particularly myopic about the desire to simplify a framework by using a container internally, rather than having to create a bunch of alternatives to a container just to satisfy a purist approach.
I simply believe that IControllerFactory and it's bretheren are MINE as an application developer to use, and that DI support shouldn't require giving that up.
Wednesday, July 2, 2014 5:59 AM -
User-1897493935 posted
Mystere
I don't think Mark is missing the point, let alone 'in several places'. He's expressing a concern about a design decision and providing code samples to back up what he's saying. I'm currently on the fence - I'd like to see some more info from Microsoft.
It's a very poor library that throws configuration totally over the wall to the library end user and says "You deal with it", particularly when it involves internal implementation objects that end user doesn't see or know anything about.I don't think anyone is advocating doing that - it's simple enough to provide a default implementation.
It's the *USE* of that factory that's the issue. I now have to choose how I want to use that extension point if I have two competing concerns that need it.How does having a container solve that? You'll still need a solution that addresses both (or more) concerns. Having a container might make it easier to not notice...
What if I want to add a DI container to an existing application, and I find that someone has already written a custom controller factory?Well, you could use it or roll your own and configure the application to use it instead.
I'm not sure why you seem to be so aggressively arguing against having a discussion - you haven't really explained what's wrong with factories and façades either, you are just very against not having a container for no discernible reason. I mean saying things like:
My point here is that you're *STEALING* extensibility from the application author by insisting these extensibility points be used for such common base functionality.Just makes no sense. Creating controllers (the example you keep using) is very common and very integral to MVC (obviously). The framework exposes an interface and affords the developer the ability to register a custom factory if they want. How on earth is that "stealing extensibility"?!
Wednesday, July 2, 2014 7:28 AM -
User1807617904 posted
Mystere, I don't understand your statements. Can you explain, preferably with code, what problem you believe that a Conforming Container solves, and why my simpler design proposals don't solve that problem?
Wednesday, July 2, 2014 10:07 AM -
User821835506 posted
Would there be any issue with MS sticking with the basic abstract factory pattern for extensibility and providing default implementations of those abstract factories which, in turn, utilize a default MS DI container?
Wednesday, July 2, 2014 1:42 PM -
User1807617904 posted
As long as that implementation is provided in a separate (satellite) assembly, it may be fine, because it would mean that the architecture doesn't depend on the concrete container. However, in the end, it depends on the abstractions defined. It's easy enough to e.g. break the Liskov Substitution Principle or the Interface Segregation Principle just by defining an interface, so keeping the implementation distinct isn't a guarantee in itself.
Wednesday, July 2, 2014 1:56 PM -
User1331021659 posted
I don't think Mark is missing the point, let alone 'in several places'. He's expressing a concern about a design decision and providing code samples to back up what he's saying.Well, he has several arguments, which are lumped into one. Some of which I agree with, some of which I don't
- The framework classes should be designed using a DI pattern - I agree
- The framework should not be designed around a Conforming Container pattern - Here we have a problem
I'm not completely sold that a conforming container is the only way to go, but Mark's ideas so far have had a decidedly incomplete feel to them and do not address the problems that a conforming container approach in a library or framework solves. His arguments against a conforming container are largely the same as arguments against Visual Basic or PHP which say "It shouldn't be used because it encourages poor design by novices". I think that's an invalid argument.
The mistake he makes is that he assumes that if you have a conforming container, your classes are designed around service location. That is not a valid assumption. You can certainly design a library completely around constructor injection, and still use a conforming container in the implementation of the façade, or factory. In fact, this is the approach that MVC3/WebAPI takes, which is to use IDependencyResolver in the IControllerFactory and other extension factories.
I don't think anyone is advocating doing that - it's simple enough to provide a default implementation.As I said (repeatedly) a default implementation only handles part of the problem. It can only construct an object. It doesn't handle the lifetime of that object. This is something containers do very well, particularly with web applications. But, in order to do that, using Mark's implementation we now force that on to the application developer who knows nothing about the internal workings of my library.
How does having a container solve that? You'll still need a solution that addresses both (or more) concerns. Having a container might make it easier to not notice...With MVC/WebAPI, Dependencies are resolved through a separate extension mechanism. NOT through extending the controller factory, so the controller factory is still available for the application to extend if he wants, even if he's using a container. Using Mark's approach, he consumes the controller factory extensibility point for DI purposes, leaving it unavailable for anything else. What's more, he also advocates taking over all the other factories as well.
Just makes no sense. Creating controllers (the example you keep using) is very common and very integral to MVC (obviously). The framework exposes an interface and affords the developer the ability to register a custom factory if they want. How on earth is that "stealing extensibility"?!The extensibility point is there for the Application author to use. It should not be used by what I consider "framework" or infrastructure code, which I consider a container to be.
If the framework is designed to use DI, then it should provide a specific DI extensibility point to hook in whatever DI framework you want to use. This should not be at the expense of other extensibility points in the framework. I'm open to this NOT being a conforming container, but I am NOT open to it being the standard Controller and other Factories.
Wednesday, July 2, 2014 3:12 PM -
User1331021659 posted
Mystere, I don't understand your statements. Can you explain, preferably with code, what problem you believe that a Conforming Container solves, and why my simpler design proposals don't solve that problem?
How about you explain to me how your proposals handle dependency lifetime management, which is something a container does.
If your argument is "Well, then hook it up to your container", then it ignores the point that the application developer doesn't know anything about the internal workings of the library or framework, and should not be responsible for configuring the lifetime of those objects. This is where the conforming container comes into play, in that a framework or library can configure itself, with the container the user has chosen to use, or a default container if they don't care.
Remember, not everything in a framework is intended to be end-user replaceable. But you may still want to use DI with them, which means you need a composition root somewhere, and the question boils down to where is that composition root in a library or framework? I argue it should be in the library, not in the application. Except for parts of the library intended to be end-user configurable.
Let's use as an example, a PDF renderer. I might have an end-user (ie application developer) visible class called PdfDocument, and look like this:
public class PdfDocument : IPdfDocument { public class Render(IMetafile metafile) {} }
When you create a PdfDocument, this is the entry point into the library, and essentially the composition root of the library. At this point, you would configure your DI for this library, which may configure dozens or even hundreds of other dependencies which I as the library user would never see.
If I use a factory to create this, it can certainly automate construction of this object. But what about all the dependencies that I, as an end user don't know about? Ok, so you advocate hiding it behind some configuration façade... but then what about object lifetime management? What about dependency graphs? You actually advocate manually instantiating all of the objects in the graphs? Possibly hundreds of them?
This just doesn't work for me. I want to design my library to depend on a container's functionality for satisfying my constructor injection. How do I do that without a conforming container and without forcing a specific container on the user?
Wednesday, July 2, 2014 3:38 PM -
User916175356 posted
I for one don't see a problem for a library to configure its object graph using a container...in fact, it should configure it however it wants, the user of said library shouldn't care as it's an internal implementation detail of the library.
However, this to me is a completely separate thing than the extensibility points of the library and the library certainly should not enforce its client to use the container IT uses internally (or even any container for that matter) in order to extend it. And here is the problem I see with the current design of asp.net vNext: it basically exposes its implementation details (which are fine if it wants to use them internally) and enforces them upon the "outside world", thereby shoehorning everyone into "its view of the world".
In fact, now that I'm writing this I realize ploeh already mentioned it in one of the previous posts by saying these concerns would be better kept separate.
Thursday, July 3, 2014 2:52 AM -
User-1897493935 posted
I definitely agree with you in places but I think we'll just have to agree to disagree with regards to the Controller factory - I see what you're saying but I just don't see it as a problem :)
I'm not sure why but I feel uneasy about a container in the framework "reaching out" to my code. It looks like the framework is going to have some all powerful object that you say: "Hey champ, when one of my controllers asks for IMyService can you give them this MyImplementation?". Why do I have to configure the framework to know how to resolve my controller's dependencies? Why should it care? All it needs to be able to do is create one. I know how to create my controllers, (possibly using a container to resolve dependencies) why shouldn't I just give the framework a factory that it can use?
As I said, definitely on the fence at the moment. I can see how having a container helps (as you pointed out, lifetime management etc.) but I feel it's my responsibility to decide if I'm going to use one and the framework shouldn't push that on me.
Thursday, July 3, 2014 4:58 AM -
User821835506 posted
I for one don't see a problem for a library to configure its object graph using a container...in fact, it should configure it however it wants, the user of said library shouldn't care as it's an internal implementation detail of the library.
However, this to me is a completely separate thing than the extensibility points of the library and the library certainly should not enforce its client to use the container IT uses internally (or even any container for that matter) in order to extend it. And here is the problem I see with the current design of asp.net vNext: it basically exposes its implementation details (which are fine if it wants to use them internally) and enforces them upon the "outside world", thereby shoehorning everyone into "its view of the world".
This is exactly what I was attempting to address in my last post. It seems to me that if the framework were architected to use abstract factories as the extensibility points but then provided default implementations of those factories which utilized the conforming container (or any default MS container) internally it would allow the type of development mystere is looking for without forcing it upon everyone. Anyone wanting to avoid the conforming container (or perhaps avoid a container all together) could simply implement the abstract factories in whichever way worked best for them (complete re-write, inherit/override default implementation replacing conforming container methods, etc).
As long as that implementation is provided in a separate (satellite) assembly, it may be fine, because it would mean that the architecture doesn't depend on the concrete container. However, in the end, it depends on the abstractions defined. It's easy enough to e.g. break the Liskov Substitution Principle or the Interface Segregation Principle just by defining an interface, so keeping the implementation distinct isn't a guarantee in itself.
I'm not sure I completely understand your concerns in this case and was hoping you could elaborate a bit. The way I see it right now with respect to my proposal, if the user is given the option to forgo the default factory implementation in favor of a custom implementation they would always have the ability to eschew any default MS container (conforming or otherwise), wouldn't they? Is the concern that the code for that MS container would still exist in the library and may tempt developers into a non-ideal design?
Thursday, July 3, 2014 10:41 AM -
User1807617904 posted
My overall concern is how the proposed design will impact my (and everyone else's) development experience. As a frequent user of ASP.NET, it's in my interest to make it as easy to use the framework as possible. The proposed design seems to me to be unnecessarily complicated, which, I'm worried will negatively impact my productivity as a user of the framework.
My first concern is that the use of a Conforming Container isn't just an optional infrastructure component. Would I mind it if it was an exclusively optional feature? No, I probably wouldn't, because it would mean that I could just ignore it. However, architectural decisions like that tends to have a big impact on the overall framework, and I would be surprised if the team could pull it off, making the Conforming Container strictly optional.
What is more likely to happen is that the presence of a Conforming Container will influence future design decisions (after all, there must be a reason why the ASP.NET team thinks this is a good idea). For example, it already seems to me that one of the reasons such a design is suggested at all, is to enable 'DI support' in attributes: the most straightforward way to do that is to rely on a Static Service Locator (an anti-pattern).
Once you introduce a Static Service Locator into the framework design, it'll spawn more designs utilizing that approach. The next most likely candidate, after attributes, is to 'DI enable' various extension methods (see e.g. this discussion on why that's a bad idea). At this point, the framework is well under way towards the Static Spider Web anti-pattern. The code will be difficult to reason about.
The problem regarding the Interface Segregation Principle (ISP) is that Service Locator breaks it. Once ISP is violated, it's also very likely that the Liskov Substitution Principle is broken, because the more members an interface defines, the more likely it becomes that various implementations change the correctness of the system (just think about implementations that throw NotSupportedException from various members, like, e.g. ReadOnlyCollection<T>).
Saturday, July 5, 2014 4:56 AM -
User1807617904 posted
How about you explain to me how your proposals handle dependency lifetime management, which is something a container does.There's nothing mysterious or magical about lifetime management that requires a DI Container; chapter 8 of my book explains how to implement various lifestyles entirely without a DI Container. Granted, some of the more exotic lifestyles like Pooled, Thread Context, Web Request Context, etc. may be a bit convoluted to implement by hand, but in my experience, you should rarely use those. The only lifestyles I use are Singleton and Per Graph. The lifestyles Singleton, Transient, and Per Graph are trivial to implement by hand, and in fact, implementing them using Pure DI gives you better insight into potential lifestyle mismatches than using a DI Container; that's one of the many reasons I prefer Pure DI over using a DI Container.
Still, if you rather prefer using a DI Container, you can do that; I've already described how to do that previously in this thread: look for the WindsorControllerFactory sample code for an example. See my DI Friendly Framework for more details, including why an Abstract Factory should also have a Release method.
When you create a PdfDocument, this is the entry point into the library, and essentially the composition root of the library.Libraries and frameworks don't have Composition Roots; only applications have Composition Roots.
In any case, I've already provided detailed descriptions of how to create a DI Friendly library. If the underlying object model is complex, the library can provide a configurable Facade. As a library user, you can use the Facade directly, or use the provided Facade methods to customize the library object - it's just a single line of code to create the object. You don't need a DI Container to do that, but you can if you will. It doesn't require a Conforming Container to enable that.
Saturday, July 5, 2014 6:05 AM -
User1331021659 posted
And here is the problem I see with the current design of asp.net vNext: it basically exposes its implementation details (which are fine if it wants to use them internally) and enforces them upon the "outside world", thereby shoehorning everyone into "its view of the world".I think you should re-evaluate the design. It doesn't do that at all.
What it does is bake DI into the framework, then it uses a conforming container interface that you can replace with any container you like. If you don't care and don't want to use DI, it uses a default internal container, and you never have to worry about it.
I really don't see how this does what you're suggesting.
Saturday, July 5, 2014 2:11 PM -
User1331021659 posted
I definitely agree with you in places but I think we'll just have to agree to disagree with regards to the Controller factory - I see what you're saying but I just don't see it as a problem :)You don't see it as a problem that if I want to use a DI container, i'm forced to give up many of my extension points? I see DI container support as being an integral part of an application framework these days. ControllerFactories were not created to add DI support, they were created to provide flexibility to the application developer. If I want to use a container *AND* I want to have my own controller factory. Even worse, let's say i'm a library author, and as part of my library, it includes a cool controller factory. Now I have to implement a version for every container out there. If DI container support is built-in, then I need only call the framework provided interface to the container the user has chosen and I can support all containers with one piece of code.
I'm not sure why but I feel uneasy about a container in the framework "reaching out" to my code. It looks like the framework is going to have some all powerful object that you say: "Hey champ, when one of my controllers asks for IMyService can you give them this MyImplementation?". Why do I have to configure the framework to know how to resolve my controller's dependencies? Why should it care? All it needs to be able to do is create one. I know how to create my controllers, (possibly using a container to resolve dependencies) why shouldn't I just give the framework a factory that it can use?You're uneasy because you are basing your opinion on incorrect assumptions. That's not what it is at all. It has a conforming container *INTERFACE*. Not that different from what MVC has today. However, unlike what they have today, they will also be providing a basic, simple, default container, which you do not have to configure if you don't want to, and don't care. You can replace this container with whatever you want if you do care. The framework doesn't force you to use a container.
I suggest you read http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx
Saturday, July 5, 2014 2:27 PM -
User1331021659 posted
It seems to me that if the framework were architected to use abstract factories as the extensibility points but then provided default implementations of those factories which utilized the conforming container (or any default MS container) internally it would allow the type of development mystere is looking for without forcing it upon everyone. Anyone wanting to avoid the conforming container (or perhaps avoid a container all together) could simply implement the abstract factories in whichever way worked best for them (complete re-write, inherit/override default implementation replacing conforming container methods, etc).That is basically how it works, so I don't really understand what your issue is. Mark's complaint is not that the controller factories are going away, which they aren't... it's the mere presence of a conforming container which he objects to. Even though that container is only used in factories.
Saturday, July 5, 2014 2:48 PM -
User1807617904 posted
if I want to use a DI container, i'm forced to give up many of my extension pointsThis, as far as I can tell, seems to be the essence of your argument. I don't understand this. Why are you forced to give up your extensibility points if a framework defines a Controller Factory?
Saturday, July 5, 2014 3:28 PM -
User1331021659 posted
ploeh
My first concern is that the use of a Conforming Container isn't just an optional infrastructure component. Would I mind it if it was an exclusively optional feature? No, I probably wouldn't, because it would mean that I could just ignore it. However, architectural decisions like that tends to have a big impact on the overall framework, and I would be surprised if the team could pull it off, making the Conforming Container strictly optional.I don't know what you mean by "exclusively" optional or "strictly" optional, but it seems to me that they are just that. You can still replace the various factories with your own implementations and it all goes away. How is that really any different from today? Of course, if you do that, you now have the responsibility of providing the right dependencies yourself.
ploeh
What is more likely to happen is that the presence of a Conforming Container will influence future design decisions (after all, there must be a reason why the ASP.NET team thinks this is a good idea).Yes, and they have expressed those reasons. From this article: http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx
Because all framework components use the same container to register services, we can now flow dependencies across the stack. This opens the door to new scenarios that were not possible before, like injecting a SignalR broadcaster into an MVC controller action.
As we walk up the stack, there are different layers of dependency resolvers. All dependencies are added to a single container and everyone can see everybody else’s services. The single container also addresses the cross-cutting concern customization story. It is trivial in the new stack to change cross-cutting concerns (e.g. logging) via a single entry point.One should note that when they say "see" they mean "resolve" so that a constructor can include dependencies from other components without caring that they're in other components.
ploeh
The problem regarding the Interface Segregation Principle (ISP) is that Service Locator breaks it.Why do you assume that this will be used for service location anywhere other than in factories, where service location is appropriate? Or, I suppose you disagree.. and this is the crux of the matter. It seems to me that you don't like containers, period. And you consider them service locators, even when used only at the root of the composition.
You're going to have a hard time convincing the rest of the world of that argument. So my problem here is that your arguments all have an anti-container bias, and thus seem self-serving. That effectively means we can't depend on your analysis of anything container related, because you flat out disagree with them under any circumstance. So it seems a bit disingenuous to argue all these other points, most of which apply to so many other technologies which we use every day.
ploeh
The next most likely candidate, after attributes, is to 'DI enable' various extension methods (see e.g. this discussion on why that's a bad idea).You can "what if" all day long. There is absolutely no evidence that any of this will be the case. I also don't understand what a discussion about moq'ing has to do with DI, especially when DI isn't mentioned in the thread at all.
Personally, I don't see how one *COULD* "DI ENABLE" an extension method. It's a static method, and can't be created, but it certainly can operation on an object that was created via injection. But that's neither here nor there. You're attempting to use random Stack overflow questions as evidence of what the Microsoft asp.net developers will do? That's really not a fair argument, now is it?
ploeh
The problem regarding the Interface Segregation Principle (ISP) is that Service Locator breaks it.You seem to be struggling to defend that position. You can't seem to come up with a good argument. Your friends argument isn't much better. Maybe you're having trouble communicating this argument because it's not really valid.
You're basing your argument on the principle that "Clients should not be forced to depend on methods they do not use", and you are claiming that because a service locator can create an infinite number of types, that somehow there is a dependency on those infinite number of types. I don't buy it. There's only a dependency if you create a dependency by introducing a type, thus anything that uses service location is only dependent upon the types it actually uses. In fact, this is directly supporting LSP. James Jensen's arguments aren't much better.
What this boils down to is that you fear that a conforming container will be used for service location. And that argument is simply wrong, because many features of languages and technologies can be abused. Good developers don't abuse features. Taking away powerful tools because they might be abused is not the way to argue.
Saturday, July 5, 2014 3:37 PM -
User916175356 posted
mystere
I think you should re-evaluate the design. It doesn't do that at all.
What it does is bake DI [n.r. implementation] into the framework, then it uses a conforming container interface that you can replace with any container you like. If you don't care and don't want to use DI, it uses a default internal container, and you never have to worry about it.
I really don't see how this does what you're suggesting.
You just answered your own question. By forcing everyone to use a conforming container (as a mechanism for implementing DI, not a particular container) it incorrectly assumes that nobody else will EVER need things which a conforming container can't provide. Additionally, it bakes the DI implementation into the framework, instead of baking in DI friendly mechanisms, which allow for any implementation. Instead I argue it should use role interfaces in the form of abstract factories, which don't actually bake the DI implementation into the framework or its users. Or, as I said before, it could use the conforming container internally as long as I don't know or care about it.
Sunday, July 6, 2014 3:17 AM -
User-1897493935 posted
Thanks for the link but I've already read it. My point was that I'm telling the framework about how to resolve my components and that feels wrong. The link you shared contains an example with the very code that makes me uneasy:
app.UseServices(services => { // Set up the dependencies services.AddTransient<IMessageGenerator, HelloMessageGenerator>(); });
MVC's responsibility is to create my controller, route a request to an action on that controller and do something with the result of that action (obviously highly simplified). MVC should have a default naive approach to creating those controllers but for advanced cases I should be able to give MVC a way of creating controllers, not have to tell it everything it needs to do so.
I know and understand that it is easy to BYOC and I can't see any situation where, if I wanted a container, I wouldn't bring my own. As soon as my app is large, I will want to do things like scan an assembly and auto register services based on conventions which the baked in implementation doesn't provide. Which I guess is the essence of my point, if you want a container, it's going to be one with advanced features. I'm happy for the framework to use a conforming container as long as I don't need to know about it.
Monday, July 7, 2014 4:04 AM -
User407763392 posted
Rob, there is nothing that prevents you from registering your own IControllerFactory implementation to replace MVC's DefaultControllerFactory. Instead of registering dependencies, just do
app.UseServices(services => { services.AddTransient<IControllerFactory, Rob89ControllerFactory>(); });
I'm happy for the framework to use a conforming container as long as I don't need to know about it.I hope that makes you happy!
Monday, July 7, 2014 12:30 PM -
User1331021659 posted
I know and understand that it is easy to BYOC and I can't see any situation where, if I wanted a container, I wouldn't bring my own. As soon as my app is large, I will want to do things like scan an assembly and auto register services based on conventions which the baked in implementation doesn't provide. Which I guess is the essence of my point, if you want a container, it's going to be one with advanced features. I'm happy for the framework to use a conforming container as long as I don't need to know about it.You seem to keep missing the point that MVC's default container is just that, a default.. You can replace it with any container you want, with all the full functionality of that container. Why do you keep arguing that you can't do this? The conforming container is an *interface*, which supplies the basic functionality that MVC needs, not what your app needs. If your app needs more functionality, then you simply use your containers native configuration. What's so hard to understand about that?
Tuesday, July 8, 2014 1:17 AM -
User-1897493935 posted
Sorry, you seem to have misunderstood what I meant to say. I was saying that I'm always going to be replacing the default container with a fully functional container using an adapter if necessary. There's nothing difficult to understand about the current design, disagreeing that a conforming container was the right way to go is a different thing to not understanding how it works.
To be honest, it's clear that Microsoft are locked into this path so we'll just see how it pans out.
Tuesday, July 8, 2014 3:32 AM -
User-1897493935 posted
I hope that makes you happy!Thanks for the example, I know how to switch to a different ControllerFactory, just not sure what the benefit of a container is versus the non service locating:
ControllerBuilder.Current.SetControllerFactory(new Rob89ControllerFactory());
Anyway - the goal is great so I'll just ignore my reservations for now.
Tuesday, July 8, 2014 3:39 AM -
User1807617904 posted
MVC's responsibility is to create my controller, route a request to an action on that controller and do something with the result of that action (obviously highly simplified). MVC should have a default naive approach to creating those controllers but for advanced cases I should be able to give MVC a way of creating controllers, not have to tell it everything it needs to do so.
I know and understand that it is easy to BYOC and I can't see any situation where, if I wanted a container, I wouldn't bring my own. As soon as my app is large, I will want to do things like scan an assembly and auto register services based on conventions which the baked in implementation doesn't provide. Which I guess is the essence of my point, if you want a container, it's going to be one with advanced features.
Yes, that's very eloquently put. It accurately reflects my thoughts as well. Thank you :)
I'm happy for the framework to use a conforming container as long as I don't need to know about it.If you don't need to know about it, it's no longer a Conforming Container ;) Like you, I also don't particularly care (or rather, I don't want to have to care) about the internal implementation details of any framework I'm using, but by making any internal composition engine public, that's exactly what happens. The definition of a Conforming Container is that it's a public API.
Tuesday, July 8, 2014 4:09 AM -
User1807617904 posted
Rob, there is nothing that prevents you from registering your own IControllerFactory implementation to replace MVC's DefaultControllerFactory. Instead of registering dependencies, just do
app.UseServices(services => { services.AddTransient<IControllerFactory, Rob89ControllerFactory>(); });
That looks innocuous, but, as I've previously explained, that API isn't particularly discoverable - as an example, how can a user discover that there's an IControllerFactory interface to override in the first place?
ASP.NET Web API has an API quite similar to the above, although it's not generic. It's used like this:
GlobalConfiguration.Configuration.Services.Replace( typeof(IHttpControllerActivator), new PureCompositionRoot());
I distinctly recall the first time I had to figure out how to do this. In order to 'discover' that the extensibility point I was interested in, was IHttpControllerActivator, I had to download the entire ASP.NET Web API source code and search through it (afterwards, I wrote this article to make it easier for other people). If the API hadn't been based on a unlimited generic method declaration, it could have been made much more discoverable.
Apart from that, my problem with the proposed API isn't that it can be used in a nice way; my problem is that it makes it easy to use it in innumerable horrible ways. This essentially break encapsulation (as the above example about ASP.NET Web API also illustrates).
Tuesday, July 8, 2014 4:22 AM -
User1807617904 posted
it's clear that Microsoft are locked into this path so we'll just see how it pans out.I'm afraid you are correct, but since this particular project is open sourced and available as a preview, I'm still regarding this as an opportunity for providing constructive feedback. That's also why I would appreciate if anyone would share some code examples of how the currently proposed architecture solves particular problems.
Tuesday, July 8, 2014 4:27 AM -
User1807617904 posted
not sure what the benefit of a container is versus the non service locating:
ControllerBuilder.Current.SetControllerFactory(new Rob89ControllerFactory());
Indeed! That's a much better and much more discoverable API than the proposed new API.
Tuesday, July 8, 2014 4:28 AM -
User-1725839221 posted
Hi everyone!
I only agree to some points stated by Mark in his posts. And I like to show you my point of view. So please let me show you my summary of the discussion of Mark here: http://wp.me/p18N2X-cc
Best regards!
Robin
Monday, November 24, 2014 9:48 AM -
User1807617904 posted
While I left a comment over on that article, I'm repeating it here for good measure:
Thank you for writing this detailed description of the planned 'DI Support' for ASP.NET vNext. It was interesting to see if I had originally misunderstood the intention and planned implementation. Sadly, this post only confirms what I already knew.
While it's a long post, I'm not going to address each point here, because it builds on two fundamentally flawed premises. Once you understand that these premises don't hold, the rest of the arguments crumble as well.
The first flaw in the reasoning is a basic misunderstanding of the Liskov Substitution Principle (LSP). The present article states that "since we are developing against interfaces, DI works on interface and the service locator works on interfaces, we can replace the used type by a more specific type."
This sentence represents a failure to understand the LSP. You should reread Robert C. Martins paper about the LSP, which contains an easy-to-understand example of how programming to an interface isn't enough
You could also refer to Krzysztof Cwalina, one of the original API designers of .NET, who explains the same type of problem without ever naming the LSP.
Sadly, it seems that the ASP.NET vNext designers have already forgotten their roots, although they are only about ten years old.
Those who cannot remember the past are condemned to repeat it.
The second flaw in the present article is that it betrays the most common, but incorrect, limited view of DI that DI is intrinsically tied to use of a DI Containter - at least in some sort of abstract form.
DI is simply the application of the Dependency Inversion Principle. If Pure DI isn't an option in a framework, it's not DI, but, instead, Service Locator, which is the opposite of DI.
Saturday, November 29, 2014 3:25 AM -
User-1725839221 posted
Hi Mark,
I am really happy to get feedback from you! Thanks a lot for it! Please excuse that the post was a little bit long, but those things need time to explain. Further, I wrote my answer to your feedback as reply in my blog and past it here, too. Do not need to link my blog post again because the link can be find above. Back to your feedback.
In your comment, you said:
"...it builds on two fundamentally flawed premises..."
Can you please explain the second one? I cannot see it, please excuse that. You are correct that DI is an application of the Dependency Inversion Principle (http://www.objectmentor.com/resources/articles/dip.pdf). But the Service Locator Pattern can be used together with dependency injection (http://martinfowler.com/articles/injection.html#UsingAServiceLocator): dependencies are injected into a container called the service locator. Then, consuming code looks for a needed service in this container, in which dependencies have been injected.
Maybe the Service Locator Pattern is the opposite (or at least something different) of DI (you should define 'opposite'), but they do not exclude each other. I have read all your posts, but I cannot find an example or argumentation that proves your statements or gives some kind of evidence. So please provide, if possible. That would help me to understand.
To your first point:
"...his sentence represents a failure to understand the LSP. You should reread Robert C. Martins paper about the LSP..."
I agree to you and revert my statement. Programming against interfaces is not enough to prevent violations against the Liskov Substitution Principle. But that is half of that what I meant. Defining interfaces in such a framework is a way to make it "simple" for developers to use it. Or say, that's the idea behind it. But it is correct, that violation of the LSP is not far away. And so the Open Close Principle is threatened what leads to more side-effects, bugs etc. in software designs.
Regarding the LSP (https://docs.google.com/file/d/0BwhCYaYDn8EgNzAzZjA5ZmItNjU3NS00MzQ5LTkwYjMtMDJhNDU5ZTM0MTlh/edit), we should talk more about behavior of software. When thinking about violations, you have to think about inheritance hierarchy - especially about base classes. So there is a problem with the LSP in general, when looking at interfaces in isolation (those problems with relaxing preconditions and strengthen post-conditions). As stated above, using interface is some kind of make it easy for developers to do things in the framework - from my point of view.
Now the questions is, what would you suggest when looking at the ASP.NET vNext framework? Using abstract base classes in the framework, from which clients derive? Maybe give them pre- und postconditions e.g. using code contracts (http://msdn.microsoft.com/en-us/library/dd264808(v=vs.110).aspx) or at least using method comments? What is YOUR proposal for the ASP.NET vNext team?
Would be nice to know what you are thinking! Thanks a lot again for your feedback! I am looking forward to the next one!
Robin
Saturday, November 29, 2014 3:41 PM