繁体   English   中英

使用 ASP.NET 核心 DI 容器编写抽象工厂实现:如何管理对象生命周期?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM