I am trying to set up a new project and I've added a new class MembershipService that requires the HttpContext to be passed in it's constructor.
In a previous project I used the code
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IMembershipService>()
.To<MembershipService>()
.InRequestScope()
.WithConstructorArgument("context", HttpContext.Current);
....
}
However in the new project I'm using Ninject Modules, and after some searching on StackOverflow and Google, I've come up with the code below: public class ServiceHandlerModule : NinjectModule {
public override void Load()
{
Bind<IMembershipService>()
.To<MembershipService>()
.WithConstructorArgument("context", ninjectContext=> HttpContext.Current);
this.Kernel.Bind(x =>
{
x.FromAssemblyContaining(typeof(NinjectWebCommon))
.SelectAllClasses()
.Where(t => t != typeof(MembershipService))
.BindDefaultInterface();
});
this.Kernel.Bind(x =>
{
x.FromAssemblyContaining<BrandServiceHandler>()
.SelectAllClasses()
.Where(t => t != typeof(MembershipService))
.BindDefaultInterface();
});
}
}
However, I get the error below:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Ninject.ActivationException: Error activating string No matching bindings are available, and the type is not self-bindable. Activation path:
5) Injection of dependency string into parameter filename of constructor of type HttpRequest
4) Injection of dependency HttpRequest into parameter request of constructor of type HttpContext
3) Injection of dependency HttpContext into parameter httpContext of constructor of type MembershipService
2) Injection of dependency IMembershipService into parameter membershipService of constructor of type HomeController
1) Request for HomeController
Can someone point out where I'm going wrong?
Thanks, John
This is where you're going wrong. The HttpContext is a runtime value, but your object graph should only consist of compile-time or configuration-time dependencies. Anything else, the runtime values, should either be passed through method calls, or should be exposed as properties from the services that are injected.
Not following this guideline, will make it much harder to compose and test your object graphs. Testing your composition root is a good example, since
HttpContext.Current
is not available when run inside a testing framework.So prevent this
MembershipService
from taking a constructor dependency onHttpContext
. Instead, inject a service that exposes theHttpContext
as a property, because this allows you to request this context after the object graph is constructor.But perhaps even better is to hide the
HttpContext
behind an abstraction that is application specific.HttpContext
is not an abstraction; it is a big and ugly API that makes your code much harder to test and much harder to comprehend. Instead, create very narrow/focused interfaces, for instance an interface like this:Now your
MembershipService
can depend on aIUserContext
that exposes aUser
object through a property. Now you can create anAspNetUserContext
implementation that uses theHttpContext.Current
internally, when theCurrentUser
property is called. This results in much cleaner, more maintainable code.Here's a possible implementation:
I agree with Steven, however, you could also:
Steven was right about the
HttpContext
being a runtime value. Its values are not even populated at the time the application is composed.This makes sense if you think about it because the application should be initialized outside of any individual user context.
However, Steven's solution only moved the problem to a different service. After all, the class that implements
IUserContext
will still need to takeHttpContext
as a dependency.The solution is to use an Abstract Factory to allow the
HttpContext
instance to be accessed at runtime instead of when the factory is wired up.With those 2 things in mind, you can create a factory for your
HttpContext
, as follows:Your
MembershipService
can then be modified to accept anIHttpContextFactory
in its constructor:And you need only inject the
HttpContextFactory
at composition time.This alone might not solve the entire issue, though. You need to ensure that the rest of your application does not try to use
HttpContext
before it is ready. In terms of DI, it means you can't useHttpContext
in any constructor of types that are composed in application start or any service members that one of those constructors calls. To solve that, you may need to create additional abstract factories to ensure those services don't call members ofIMembershipService
untilHttpContext
is ready.See this answer for more information about how to accomplish that.
Steven's solution also entailed creating a Facade around
HttpContext
. While this does not really help solve the problem at hand, I agree that this might be a good idea if yourMembershipService
(and perhaps other services) only uses a small number of members ofHttpContext
. Generally, this pattern is to make a complex object simpler to work with (such as flattening it down to a few members that may be nested deep within its hierarchy). But you really need to weigh the extra maintenance of adding another type against the complexity of usingHttpContext
within your application (or the value of swapping out a section of it) to make that decision.