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
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 take HttpContext
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.
Important: HttpContext is not an abstraction, so it cannot be swapped or mocked. To ensure we are dealing with an abstraction, Microsoft has provided the HttpContextBase abstract class and the default concrete type HttpContextWrapper. HttpContextBase has exactly the same interface as HttpContext. You should always use HttpContextBase as the abstract reference type within your services, not HttpContext.
With those 2 things in mind, you can create a factory for your HttpContext
, as follows:
public interface IHttpContextFactory
{
HttpContextBase Create();
}
public class HttpContextFactory
: IHttpContextFactory
{
public HttpContextBase Create()
{
return new HttpContextWrapper(HttpContext.Current);
}
}
Your MembershipService
can then be modified to accept an IHttpContextFactory
in its constructor:
public class MembershipService : IMembershipService
{
private readonly IHttpContextFactory httpContextFactory;
// This is called at application startup, but note that it
// does nothing except get our service(s) ready for runtime.
// It does not actually use the service.
public MembershipService(IHttpContextFactory httpContextFactory)
{
if (httpContextFactory == null)
throw new ArgumentNullException("httpContextFactory");
this.httpContextFactory = httpContextFactory;
}
// Make sure this is not called from any service constructor
// that is called at application startup.
public void DoSomething()
{
HttpContextBase httpContext = this.httpContextFactory.Create();
// Do something with HttpContext (at runtime)
}
}
And you need only inject the HttpContextFactory
at composition time.
kernel.Bind<IHttpContextFactory>()
.To<HttpContextFactory>();
kernel.Bind<IMembershipService>()
.To<MembershipService>();
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 use HttpContext
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 of IMembershipService
until HttpContext
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 your MembershipService
(and perhaps other services) only uses a small number of members of HttpContext
. 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 using HttpContext
within your application (or the value of swapping out a section of it) to make that decision.
I've added a new class MembershipService that requires the HttpContext
to be passed in it's constructor.
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 on HttpContext
. Instead, inject a service that exposes the HttpContext
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:
public interface IUserContext
{
User CurrentUser { get; }
}
Now your MembershipService
can depend on a IUserContext
that exposes a User
object through a property. Now you can create an AspNetUserContext
implementation that uses the HttpContext.Current
internally, when the CurrentUser
property is called. This results in much cleaner, more maintainable code.
Here's a possible implementation:
public class AspNetUserContext : IUserContext
{
public User CurrentUser
{
// Do not inject HttpContext in the ctor, but use it
// here in this property
get { return new User(HttpContext.Current.User); }
}
}
I agree with Steven, however, you could also:
kernel.Bind<HttpContext>().ToMethod(c => HttpContext.Current);