【问题标题】:Autofac parameterized instantiation that resolves differently for different parametersAutofac 参数化实例化,针对不同的参数以不同的方式解析
【发布时间】:2018-01-13 21:12:30
【问题描述】:

我正在将 Autofac 与 ASP.NET Core 一起使用。

我的依赖是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" 的文档说

多次解析对象,无论传入不同的参数,每次都会得到相同的对象实例。只是传递不同的参数不会破坏对生命周期范围的尊重。

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

【问题讨论】:

  • @mjwills 嗯,我需要调查一下,我希望我想要的东西是可能的,我只是使用了错误的语法或其他东西。
  • 您的Person 似乎是域实体;不是应用程序组件。 DI 容器旨在构建组件的对象图。在这些对象图的构建过程中,不应该存在关于其依赖关系的歧义,这不包括使用运行时数据,例如实体、DTO、视图模型和其他数据对象。
  • @Steven 这只是一个例子。我已经改了名字。问题还是一样。

标签: c# dependency-injection asp.net-core autofac ioc-container


【解决方案1】:

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

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

问题是,如果你想将它注入另一个类,你必须用IIndex&lt;bool, IReporter&gt; 注入它:

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);
    }
}

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

【讨论】:

  • 这个解决方案很好地适用于布尔输入,但是如果输入更加动态,比如整数呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多