Autofac 参数化实例化,针对不同的参数进行不同的解析

Autofac parameterized instantiation that resolves differently for different parameters

我正在使用带有 ASP.NET Core 的 Autofac。

我的依赖是 Reporter:

public class Reporter {
  public Reporter (bool doLogging) { DoLogging = doLogging ; }
  public string DoLogging { get; set; }
  // other stuff
}

我需要这样使用它:

public class Foo
{
  public Foo(Func<bool, Reporter> reporterFactory) { _reporterFactory = reporterFactory; }
  private readonly Func<bool, Reporter> _reporterFactory;
}

我希望它像这样解决:

_reporterFactory(false) ---> equivalent to ---> new Reporter(false)
_reporterFactory(true)  ---> equivalent to ---> new Reporter(true)

对于相同的 bool 参数,我希望每个请求都有相同的实例(即 Autofac 的 InstancePerLifetimeScope)。当我多次调用 _reporterFactory(false) 时,我想要同一个实例。当我多次调用 _reporterFactory(true) 时,我想要同一个实例。但是这两个实例必须彼此不同。

所以我这样注册:

builder
  .Register<Reporter>((c, p) => p.TypedAs<bool>() ? new Reporter(true): new Person(false))
  .As<Reporter>()
  .InstancePerLifetimeScope();    // gives "per HTTP request", which is what I need

但是,当我解析时,无论 bool 参数如何,我都会得到相同的实例:

var reporter            = _reporterFactory(false);
var reporterWithLogging = _reporterFactory(true);
Assert.That(reporter, Is.Not.SameAs(reporterWithLogging));     // FAIL!

"Parameterized Instantiation" 的文档说

resolve the object more than once, you will get the same object instance every time regardless of the different parameters you pass in. Just passing different parameters will not break the respect for the lifetime scope.

这解释了行为。那么如何正确注册呢?

如评论中所述,您可以使用 keyed 服务来实现您的目标:

builder.Register(c => new Reporter(true)).Keyed<IReporter>(true).InstancePerLifetimeScope();
builder.Register(c => new Reporter(false)).Keyed<IReporter>(false).InstancePerLifetimeScope();

问题是,如果你想将它注入另一个 class,你必须用 IIndex<bool, IReporter>:

注入它
public class Foo
{
    public Foo(IIndex<bool, IReporter> reporters)
    {
        var withLogging = reporters[true];
        var withoutLogging = reporters[false];
    }
}

IIndex是Autofac的接口,它让你的组件和容器紧密耦合,这可能不是我们想要的。为避免这种情况,您可以另外注册工厂,如下所示:

builder.Register<Func<bool, IReporter>>((c,p) => withLogging => c.ResolveKeyed<IReporter>(withLogging)).InstancePerLifetimeScope();

public class Foo
{
    public Foo(Func<bool, IReporter> reporters)
    {
        var withLogging = reporters(true);
        var withoutLogging = reporters(false);
    }
}

现在您有了无需耦合到容器本身的工作解决方案。