[英]How to inject multiple service class implementation of single interface in class using default asp.net core DI container
[英]Write an abstract factory implementation by using the ASP.NET core DI container: how to manage object lifetime?
我们正在将庞大的代码库从 .NET 框架迁移到 .NET 核心。 不幸的是,我们正在迁移的一些代码受到设计味道的影响,但我们不能在这个阶段随意破坏东西,我们需要随着时间的推移仔细计划更改。
主要问题之一是我们的代码依赖于大量的抽象工厂,这些抽象工厂被称为代码异味(至少在被滥用时)。 一个相关的问题是 Castle Windsor 容器在现有代码中被广泛使用,我们希望避免在 ASP.NET 核心中使用它,我们更喜欢坚持使用默认的内置 DI 容器。
我试图了解是否可以通过使用 ASP.NET 核心 DI 容器重写我们的一些抽象工厂实现,目前基于城堡windsor 容器。 根据我对依赖注入的理解,在应用程序的组合根中编写这些类不是问题; 换句话说,拥有一个依赖于 DI 容器的组合根类并不是一种引入服务定位器代码气味的方法。
目前,我们抽象工厂的接口定义是一个有漏洞的,因为它公开了一个Release
方法,该方法仅用于遵守 Castle Windsor 的register resolve release 模式。 这是当前的接口定义:
public interface ICommandHandlerFactory
{
ICommandHandler CreateHandler(Type commandType);
void Release(ICommandHandler handler);
}
具体实现取决于 Castle Windsor 容器,以便在用户完成时解析命令处理程序并释放它们:
public class WindsorCommandHandlerFactory
{
private readonly IKernel _container;
public WindsorCommandHandlerFactory(IKernel container)
{
_container = container;
}
public ICommandHandler CreateHandler(Type commandType)
{
// here we create the command handler type from the command type and then
// we ask the container to resolve the command handler type
}
public void Release(ICommandHandler handler)
{
_container.ReleaseComponent(handler);
}
}
我的问题与对象生命周期管理有关。 关键是对于 ASP.NET 核心 DI 容器,生命周期管理不是基于Release
方法,而是基于scope的概念。
最佳实践基本上是从应用程序根容器创建作用域,从作用域的容器中解析依赖项,使用依赖项并最终处置作用域。 当作用域被释放时,作用域内解析的作用域和瞬态服务将被停用并避免内存泄漏。
这是对应的代码:
public class Worker
{
private readonly IServiceProvider container;
public Worker(IServiceProvider container)
{
_container = container;
}
public void DoStuff()
{
using(var scope = container.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IService>();
service.Work();
}
}
}
为了坚持这种设计,我应该通过删除泄漏(现在不再有用)的Release
方法来明确简化抽象工厂定义:
public interface ICommandHandlerFactory
{
ICommandHandler CreateHandler(Type commandType);
}
鉴于此接口定义,我如何处理容器作用域的创建和处置?
我不能简单地做以下事情,因为当作用域被释放时,解析的依赖项将被停用,因此调用代码可能有一个对已释放对象的引用:
// this WON'T work due to the scope disposal when the service is returned
public class CommandHandlerFactory
{
private readonly IServiceProvider container;
public CommandHandlerFactory(IServiceProvider container)
{
_container = container;
}
public ICommandHandler CreateHandler(Type commandType)
{
using(var scope = container.CreateScope())
{
Type commandHandlerType = ... // build the command handler type starting from the command type
var service = scope.ServiceProvider.GetRequiredService(commandHandlerType);
return service;
}
}
}
你有什么主意吗 ?
再次仔细阅读这篇文章后,我意识到这个设计问题的解决方案非常明显。
讨论的重点是抽象工厂设计模式的内在问题,因此解决的方法可能是完全避免抽象工厂。
以下代码非常具有自动解释性,并显示了一种用另一个抽象(在示例中称为ICommandDispatcher
)替换抽象工厂的方法,该抽象基本上是一个适配器,用于为要处理的命令调用正确的命令处理程序。
public interface ICommand
{
}
public interface ICommandHandler<T> where T : ICommand
{
void Handle(T command);
}
public interface ICommandDispatcher
{
void Dispatch(ICommand command);
}
public class CommandDispatcher : ICommandDispatcher
{
private readonly IServiceProvider _container;
public CommandDispatcher(IServiceProvider container)
{
_container = container ?? throw new ArgumentNullException(nameof(container));
}
public void Dispatch(ICommand command)
{
if (command == null)
throw new ArgumentNullException(nameof(command));
var commandType = command.GetType();
var commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
using (var scope = _container.CreateScope())
{
var commandHandler = scope.ServiceProvider.GetRequiredService(commandHandlerType);
((dynamic)commandHandler).Handle((dynamic)command);
}
}
}
现在,以前依赖于ICommandHandlerFactory
所有代码都应该依赖于新的抽象ICommandDispatcher
。 只需记住在应用程序的组合根中定义类CommandDispatcher
(组合根是唯一允许依赖 IOC 容器的代码模块)。
简单地说,工厂接管了 DI 容器。 当您使用工厂时,工厂管理它创建的事物的生命周期。 您可以使用 DI 容器来管理factory的生命周期,但这就是它停止的地方。
您的问题真的太广泛了,无法在不为您重写代码的情况下合理回答。 但是,从本质IDisposable
,工厂应该实现IDisposable
。 它应该更新它负责的事情,然后在Dispose
方法实现中处理它们。 从Microsoft.Extensions.DependencyInjection
方面,您将工厂注册为单例(以便它可以在应用程序生命周期的整个范围内管理生命周期,因为此时它再次接管了 DI 容器的角色)。 然后,您可以在任何需要的地方注入工厂,并使用它来创建您需要的东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.