Как внедрить свойство в класс, который я не контролирую создание
Как я могу ввести свойство IServiceManager в этот класс с помощью autofac? Это пользовательский класс фабрики поставщиков ресурсов, который вызывается при вызове HttpContext.GetGlobalResourceObject("", "MyResource")
чтобы получить строку ресурса.
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)
{
...
}
}
1 ответ
Я столкнулся с такой же проблемой - с ASP.NET ResourceProviderFactory
- и ответ, к сожалению, заключается в том, что вы должны использовать услугу определения местоположения.
В любом месте нет "зацепки" в конвейер, когда вы можете что-нибудь добавить или изменить встроенное поведение ASP.NET. Таким образом, если вам нужно что-то поместить в свойство, оно должно быть установлено в конструкторе.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
public IServiceManager ServiceManager { get; set; }
public SqlResourceProviderFactory()
{
this.ServiceManager =
DependencyResolver.Current.GetService<IServiceManager>();
}
}
Да, это действительно ужасно
Здесь очень важно учитывать, особенно в отношении Autofac, срок службы, для которого IServiceManager
зарегистрирован.
ResourceProviderFactory
создается один раз и кэшируется. То же самое с глобальными / локальными поставщиками ресурсов, которые выходят из методов Create *. у нас было чертовски время с этим, потому что это означает, что не обязательно HttpContext
в то время, когда фабрики создаются, и даже если таковые имеются, если какие-либо из нижестоящих зависимостей зарегистрированы InstancePerHttpRequest
тогда они будут утилизированы, и вы будете в безопасности.
- Все, что используется вашим
ResourceProviderFactory
или сгенерированные поставщики ресурсов - все время вниз по стеку - должны быть зарегистрированы либоSingleInstance
или жеInstancePerDependency
, - Если возможно, вместо использования
DependencyResolver.Current
(что для Autofac требует активногоHttpContext
), ссылка на контейнер приложения напрямую. Это означает, что вам нужно хранить ссылку на него где-то еще (глобальная статическая переменная?) И использовать его.
Вот что может включать в себя более полное решение:
// 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>();
}
}
Обратите внимание, что, поскольку вы решаете это из корневого контейнера, он останется на всю жизнь приложения. Даже если вы зарегистрируете его как InstancePerDependency
из-за внутреннего кэширования.NET он создается только один раз.
Если вам не нравится создавать свой собственный класс статического держателя, вы можете абстрагировать его, используя CommonServiceLocator
и Autofac.Extras.CommonServiceLocator
пакеты.