2
votes

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

3
From the exception message it seems the parameter is called httpContext... so try it with .WithConstructorArgument("httpContext", ninjectContext=> HttpContext.Current);nemesv

3 Answers

15
votes

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.

6
votes

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); }
    }
}
2
votes

I agree with Steven, however, you could also:

kernel.Bind<HttpContext>().ToMethod(c => HttpContext.Current);