views:

682

answers:

5

Hi,

I have a custom HTTP Module. I would like to inject the logger using my IoC framework, so I can log errors in the module. However, of course I don't get a constructor, so can't inject it into that. What's the best way to go about this?

If you need the specific IoC container - I'm currently using Windsor, but may soon move to AutoFac.

Thanks

+2  A: 

You could use a service locator, take a look at this question:

Difference between dependency injection and service locator pattern

FinnNk
+1  A: 

You could pass in the required dependencies via the HttpApplication context passed to you by the Init method...

public class MyHttpModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        var dependency = (IDependency)context.Context.Items["dependency"];
        // consume dependency...
    }

    public void Dispose()
    {
    }
}
Mark Seemann
-1 because it's very hard to mock out dependencies this way
Mauricio Scheffer
@Mauricio Scheffer: Perhaps, but then just implement the module as a Humble Executable. I prefer that approach over using static injection. People think it helps testability, but it really hurts it.
Mark Seemann
Humble Executable certainly works well in this case, I would add that piece of information to the answer. However, you can have your cake and eat it too, see my solution.
Mauricio Scheffer
+4  A: 

First time I saw dependency injection to HttpModules in Spring.NET (not advertising this framework though). The idea is that you have special HttpModule which injects dependencies to other application-level HttpModule-s.

Unfortunatelly current version of Autofac.Integration.Web does not support this, but you can easily do that yourself:

public class MyModule : IHttpModule
{
    public void Dispose()
    {            
    }

    public void Init(HttpApplication context)
    {
        Assert.IsNotNull(MyService);
    }        

    public IMyService MyService { get; set; }
}

public class HttpModuleInjectionModule : IHttpModule
{
    public void Dispose()
    {            
    }

    public void Init(HttpApplication context)
    {
        var containerProviderAccessor = context as IContainerProviderAccessor;

        if(containerProviderAccessor == null)
            throw new InvalidOperationException("HttpApplication should implement IContainerProviderAccessor");

        var rootContainer = containerProviderAccessor.ContainerProvider.ApplicationContainer;

        foreach (string moduleName in context.Modules.AllKeys)
            rootContainer.InjectProperties(context.Modules[moduleName]);
    }
}

public class Global : HttpApplication, IContainerProviderAccessor
{
  static IContainerProvider _containerProvider;

  protected void Application_Start(object sender, EventArgs e)
  {
    var builder = new ContainerBuilder();
    builder.Register<MyService>().As<IMyService>();
    _containerProvider = new ContainerProvider(builder.Build());
  }

  public IContainerProvider ContainerProvider
  {
    get { return _containerProvider; }
  }
}

HttpModuleInjectionModule should be registered before other HttpModule-s in web.config:

  <httpModules>
   <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
   <add name="HttpModuleInjection" type="WebTest.HttpModuleInjectionModule, WebTest"/>
   <add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
   <add name="PropertyInjection" type="Autofac.Integration.Web.PropertyInjectionModule, Autofac.Integration.Web"/>
   <add name="MyModule" type="WebTest.MyModule, WebTest"/>
  </httpModules>

I'm sure you can do similar things in Windsor. The difference would be in how you access your root container from HttpModuleInjectionModule.

this is pretty limited, can't do constructor injection.
Mauricio Scheffer
A: 

I am curious about andrey-tsykunov's answer, but don't have the rep to comment on it.

I am trying to get comfortable with IoC and DI, so I may be missing something, but wouldn't it be simpler to use IContainerProviderAccessor from within MyModule, rather than create another module?

For example:

public class MyModule : IHttpModule
{
    public void Dispose()
    {            
    }

    public void Init(HttpApplication context)
    {
        Assert.IsNotNull(MyService);

        var containerProviderAccessor = context as IContainerProviderAccessor;

        if (accessor != null)
        {
            IContainer container = containerProviderAccessor.ContainerProvider.ApplicationContainer;
            MyService = container.Resolve<IMyService>();
        }
    }

    private IMyService MyService { get; set; }
}
adrift
You're not doing dependency injection here, you're doing service location.
Mauricio Scheffer