【问题标题】:Passing Services using Dependency Injection and Factory Pattern in ASP.NET在 ASP.NET 中使用依赖注入和工厂模式传递服务
【发布时间】:2017-03-23 15:24:47
【问题描述】:

我使用的是 ASP.NET Core,我知道框架已经提供了这样的 Logging 机制,但是用这个来说明我的问题。

我正在使用一种工厂模式来构建 Logger 类,因为我不知道日志记录的类型(因为它存储在 DB 中)。

ILogger 合同

Log(string msg)

那么LoggerFactory会根据DB传入的参数创建Logger后返回一个ILogger:

public class LoggerFactory
{
    public static Contracts.ILogger BuildLogger(LogType type)
    {
        return GetLogger(type);
    }

//other code is omitted, GetLogger will return an implementation of the related logger

现在,当我需要使用 Logger 时,我必须这样做:

  public class MyService
{
    private ILogger _logger
    public MyService()
    {
        _logger = LoggerFactory.BuildLogger("myType");
    }

但是,我打算保留我的类而不进行任何实例化,我需要在 MyService 中使用 Constructor DI,并且我需要在 Startup 上注入所有依赖项:

    services.AddTransient<Contracts.ILogger, LoggerFactory.BuildLogger("param") > ();

但这行不通,我们需要通过一个具体的实现。 如何使用 DI 来完成这项工作,有没有更好的方法来实现它?

【问题讨论】:

  • 一种选择是注入LoggerFactory,而不是ILogger。这样,您将获得 LoggerFactory,从您的 DB 中获取类型并构建您的 ILogger。如果要注入 ILogger,则必须在注入时知道 ILogger 的类型。但您可能还没有此信息。
  • 您的意思是在 MyService 构造函数中传递 LoggerFactory 并且不要将其添加到 .net 核心服务中
  • 是的。像这样:services.AddSingleton&lt;LoggerFactory&gt;();。单例意味着您将仅在第一次需要时创建 LoggerFactory 对象。在此之后,您将只使用相同的。然后,你可以这样使用:public MyService(LoggerFactory loggerFactory) {....}

标签: c# asp.net-mvc dependency-injection asp.net-core factory-pattern


【解决方案1】:

你的方法有几个错误:

相反,您的服务应如下所示:

public class MyService
{
    private ILogger _logger;
    public MyService(ILogger logger)
    {
        _logger = logger;
    }
}

这极大地简化了依赖ILogger 的所有消费者。这也意味着为MyService 获取正确的ILogger 成为Composition Root 的责任,这是拥有这些知识的正确地方。

这确实意味着您可能需要从 ASP.NET Core 的内置 DI 容器转移到功能更丰富的 DI 库,因为内置容器无法进行上下文感知注册ILogger 同时让库自动连接其他构造函数依赖项。

使用 ASP.NET Core DI 容器,您只能使用委托手动连接您的服务。例如:

services.AddTransient<MyService>(c => new MyService(
    BuildLogger(typeof(MyService).Name),
    c.GetRequiredService<ISomeOtherDependency>(),
    c.GetRequiredService<IYetAnotherOne>());

【讨论】:

  • Steven,在你的例子中,他不能注入正确的 ILogger,对吧?如果他在实例化MyService之前知道类型,那么他可以将构造函数更改为public MyService(ILogger&lt;TypeA&gt; logger)。但既然他不知道,他必须注入一个可以为他做到这一点的接口。如果我错了,请纠正我。
  • @FabricioKoch:我不确定我是否关注你。除此之外,我不建议在这里引入泛型抽象,因为MyService 对这种泛型类型完全不感兴趣;它只是想记录。因此,提供正确类型是组合根的问题,而不是消费者的问题。
  • @Steven,我从你那里了解到,这对于 ASP Core DI 容器是不可行的,因为它不提供组合根?如果答案是肯定的,那么如何使用 Ninject 或 AutoFac 等其他库来做到这一点?
  • @h.salman 每个应用程序都包含一个组合根。最好的方法是将框架的 ILogger 抽象隐藏在特定于应用程序的抽象后面,如here 所述,并实现一个到 MS.Extensions.Logging 的适配器,如here 所述。 This documentation 解释了如何使用 Simple Injector 连接此适配器。如果您想知道如何使用 Ninject 或 Autofac 进行连接,则必须使用 Google。
  • @h.salman:您不应该像you should not construct components using runtime data 那样基于运行时数据构建对象图。而是为您的ILogger 抽象创建一个代理实现,该实现可以调用数据库并将调用转发到实际实现。通过这种方式,您可以在不使 ILogger 的消费者复杂化的情况下添加它,并防止您的合成根复杂化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-05
相关资源
最近更新 更多