在 ASP.NET 中使用依赖注入和工厂模式传递服务
Passing Services using Dependency Injection and Factory Pattern in ASP.NET
我正在使用ASP.NET Core,我知道框架已经提供了这样的Logging机制,但是用这个来说明我的问题。
我正在使用一种工厂模式来构建记录器 class,因为我不知道记录的类型(因为它存储在数据库中)。
ILogger 合约
Log(string msg)
然后 LoggerFactory 将 return 在根据从 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");
}
但是,我打算保留我的 classes 而不进行任何实例化,我需要在 MyService 中使用 Constructor DI,并且我需要在 Startup 上注入所有依赖项:
services.AddTransient<Contracts.ILogger, LoggerFactory.BuildLogger("param") > ();
但这行不通,我们需要通过一个具体的实现。
如何使用 DI 使其工作,是否有更好的实现方法?
您的方法有几个错误:
- 您的服务依赖于具体的
LoggerFactory
类型,这是 Dependency Inversion Principle 违规行为。
- 进行这种额外的初始化会使构建对象图变得不可靠,而 injection constructors should be simple。
- 它隐藏了一个事实,即
ILogger
是您的消费者所依赖的真正服务。这使得系统更难测试、更难维护,并使对象图分析复杂化。
- 工厂的使用是有味道的,因为factories are hardly ever the right solution.
相反,您的服务应如下所示:
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 核心 DI 容器,您只能使用委托手动连接您的服务。例如:
services.AddTransient<MyService>(c => new MyService(
BuildLogger(typeof(MyService).Name),
c.GetRequiredService<ISomeOtherDependency>(),
c.GetRequiredService<IYetAnotherOne>());
我正在使用ASP.NET Core,我知道框架已经提供了这样的Logging机制,但是用这个来说明我的问题。
我正在使用一种工厂模式来构建记录器 class,因为我不知道记录的类型(因为它存储在数据库中)。
ILogger 合约
Log(string msg)
然后 LoggerFactory 将 return 在根据从 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");
}
但是,我打算保留我的 classes 而不进行任何实例化,我需要在 MyService 中使用 Constructor DI,并且我需要在 Startup 上注入所有依赖项:
services.AddTransient<Contracts.ILogger, LoggerFactory.BuildLogger("param") > ();
但这行不通,我们需要通过一个具体的实现。 如何使用 DI 使其工作,是否有更好的实现方法?
您的方法有几个错误:
- 您的服务依赖于具体的
LoggerFactory
类型,这是 Dependency Inversion Principle 违规行为。 - 进行这种额外的初始化会使构建对象图变得不可靠,而 injection constructors should be simple。
- 它隐藏了一个事实,即
ILogger
是您的消费者所依赖的真正服务。这使得系统更难测试、更难维护,并使对象图分析复杂化。 - 工厂的使用是有味道的,因为factories are hardly ever the right solution.
相反,您的服务应如下所示:
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 核心 DI 容器,您只能使用委托手动连接您的服务。例如:
services.AddTransient<MyService>(c => new MyService(
BuildLogger(typeof(MyService).Name),
c.GetRequiredService<ISomeOtherDependency>(),
c.GetRequiredService<IYetAnotherOne>());