How do I inject the IServiceManager property into this class using autofac? This is a custom resource provider factory class that gets called when you make a call to HttpContext.GetGlobalResourceObject("", "MyResource")
to get a resource string.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
// needs autofac property injection
public IServiceManager ServiceManager { get; set; }
public override IResourceProvider CreateGlobalResourceProvider(string classKey)
{
...
}
public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
...
}
public static string GetAppRelativePath(string logicalPath)
{
...
}
}
I've faced this exact same problem - with an ASP.NET ResourceProviderFactory
- and the answer is, unfortunately, that you have to use service location.
There's no "hook" into the pipeline anywhere that you can inject anything or change the built-in ASP.NET behavior. Thus, if you need something put into a property, it has to be set in the constructor.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
public IServiceManager ServiceManager { get; set; }
public SqlResourceProviderFactory()
{
this.ServiceManager =
DependencyResolver.Current.GetService<IServiceManager>();
}
}
Yeah, it's really ugly.
Something very important to consider here, especially with respect to Autofac, is the lifetime scope for which your IServiceManager
is registered.
The ResourceProviderFactory
is created once and cached. Same with the global/local resource providers that come out of the Create* methods. we had a heck of a time with this because it means there's not necessarily an HttpContext
at the time the factories get created, and even if there is, if any of the downstream dependencies are registered InstancePerHttpRequest
then they'll be disposed of and you're hosed.
ResourceProviderFactory
or generated resource providers - all the way down the stack - should be registered either SingleInstance
or InstancePerDependency
.DependencyResolver.Current
(which, for Autofac, requires an active HttpContext
), reference the application container directly. That means you need to store a reference to it somewhere else (a global static variable?) and use that.This is what a more complete solution might involve:
// Create some "holder" for the app container.
public static class ApplicationContainerProvider
{
public static ILifetimeScope Container { get; set; }
}
// In Global.asax, build your container and set it in both
// the DependencyResolver AND in the holder class.
var builder = new ContainerBuilder();
builder.RegisterType<Something>().As<ISomething>();
var container = builder.Build();
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
ApplicationContainerProvider.Container = container;
// In your service location, reference the container instead of
// DependencyResolver.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
public IServiceManager ServiceManager { get; set; }
public SqlResourceProviderFactory()
{
this.ServiceManager =
ApplicationContainerProvider.Container.Resolve<IServiceManager>();
}
}
Note that since you're resolving that out of the root container, it'll stick around for the lifetime of the application. Even if you register it as InstancePerDependency
, because of the internal caching .NET does, it'll only get created once.
If you don't like creating your own static holder class like that, you can abstract it away by using the CommonServiceLocator
and the Autofac.Extras.CommonServiceLocator
packages.