【问题标题】:How to inject a property into a class that I don't control the creation of如何将属性注入到我不控制创建的类中
【发布时间】:2014-02-24 16:53:07
【问题描述】:

如何使用 autofac 将 IServiceManager 属性注入此类?这是一个自定义资源提供程序工厂类,当您调用 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)
    {
        ...
    }
}

【问题讨论】:

    标签: c# dependency-injection autofac property-injection


    【解决方案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 或生成的资源提供程序使用的任何东西(一直到堆栈)都应该注册SingleInstanceInstancePerDependency
    • 如果可能,请直接引用应用程序容器,而不是使用 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 的内部缓存,它只会被创建一次。

    如果您不喜欢这样创建自己的静态持有者类,可以使用 CommonServiceLocatorAutofac.Extras.CommonServiceLocator 包将其抽象出来。

    【讨论】:

    • 我已经在使用服务位置只是为了“偷偷摸摸”,直到找到正确的 DI 方法,所以没有办法真是糟透了。我考虑过创建一个自定义 ResourceManager 来绕过 .Net 的内置版本,但它的工作量很大,仍在争论中。您已经指出了我还没有考虑过的生命周期范围的问题。我要试一试这个单例容器,看看它是怎么回事。不过,在应用程序期间持有 DbContext 实例感觉不太对。
    • 嗨特拉维斯,我在尝试这个时遇到了各种各样的麻烦。大量No scope with a Tag matching 'AutofacWebRequest' 消息和其他各种消息。我在想如果我只是这样做:public IServiceManager ServiceManager {get{return DependencyResolver.Current.GetService&lt;IServiceManager&gt;();}} 它总是会出去并为我获得一个新的 IServiceManager 对吗?即使 SqlResourceProviderFactory 被缓存,它总是会解析 IServiceManager。
    • 这些例外是我提到你不能使用 DependencyResolver.Current 的原因 - AutofacDependencyResolver 需要 一个网络上下文,当它被创建时你可能不会有一个。而且,服务管理器的整个依赖链中的nothing都可以注册InstancePerHttpRequest。
    • 不要忘记 - 您不一定每次都获得新的资源管理器。 ASP.NET 会缓存这些东西,即使使用工厂也是如此,因此拥有这样的动态属性对您没有帮助。真的,可以把它想象成一个为整个应用程序解析一次的单例。您只需一枪即可创建它们,仅此而已。
    • 好的,我想我现在的问题是我需要两个容器。一个静态的用于特殊雪花,例如ResourceProviderFactory,另一个用于需要 InstancePerHttpRequest() 范围的应用程序的其余部分。我将开始一个新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-16
    相关资源
    最近更新 更多