繁体   English   中英

如何使用 .NET CORE DI 为同一接口注册多个实现

[英]How to register multiple implementation for same interface using .NET CORE DI

我有一个名为 ICompleteConsumer 的接口,它只有一个方法,并且该接口有多个实现。

前任:

public class SampleClass1: ICompleteConsumer {

 public async Task Complete(Model model) {
  // do the work
 }
}

public class SampleClass2: ICompleteConsumer {

 public async Task Complete(Model model) {
  // do the work
 }
}

我像这样在启动中注册了这些

services.AddScoped<ICompleteConsumer, SampleClass1>(),
services.AddScoped<ICompleteConsumer, SampleClass2>(), 

我注入这些依赖项如下

public class ConsumerHandlerService(string message) {

 private readonly ICompleteConsumer completeConsumer;

 public ConsumerHandlerService(ICompleteConsumer completeConsumer) {
  this.completeConsumer = completeConsumer
 }

 switch (MessageType) {
  case (MessageType .1) //I want SampleClass1 implementation  to here
  var model = JsonConvert.DeserializeObject < Model > (message);
  await completeConsumer.complete(model);
  break;

  case (MessageType .2) // I want SampleClass2 implementation to here
  var model = JsonConvert.DeserializeObject < Model > (message);
  await completeConsumer.complete(model);
  break;
 }
}

如何使用 .net 核心 DI 实现这一目标?

您的问题实际上有两点讨论。 一个与代码设计有关,另一个与如何使用 .NET 核心 DI 容器以处理所需的注册有关。 两者都很重要,但我们需要一次处理一个。

如何组织代码

要以一种干净且可扩展的方式解决您的问题,您需要使用一种称为复合设计模式的设计模式。 为此,您需要将接口的定义更改为以下内容:

public interface IMessageConsumer 
{
  bool CanHandleMessage(Message message);
  Task HandleMessage(Message message);
}

然后您的接口实现更改如下:

public class FooMessageConsumer: IMessageConsumer 
{
  public bool CanHandleMessage(Message message)
  {
    if (message is null) throw new ArgumentNullException(nameof(message));

    return message.Type == "foo";
  }

  public Task HandleMessage(Message message)
  {
    if (message is null) 
        throw new ArgumentNullException(nameof(message));

    if (!this.CanHandleMessage(message)) 
        throw new InvalidOperationException($"{nameof(FooMessageConsumer)} can only handle foo messages.");

    await Task.Delay(100).ConfigureAwait(false);

    Console.Writeline($"Message {message.Id} handled by {nameof(FooMessageConsumer)}");
  }
}

public class BarMessageConsumer: IMessageConsumer 
{
  public bool CanHandleMessage(Message message)
  {
    if (message is null) throw new ArgumentNullException(nameof(message));

    return message.Type == "bar";
  }

  public Task HandleMessage(Message message)
  {
    if (message is null) 
        throw new ArgumentNullException(nameof(message));

    if (!this.CanHandleMessage(message)) 
        throw new InvalidOperationException($"{nameof(BarMessageConsumer)} can only handle bar messages.");

    await Task.Delay(100).ConfigureAwait(false);

    Console.Writeline($"Message {message.Id} handled by {nameof(BarMessageConsumer)}");
  }
}

此时需要引入一个特殊的消息消费者,用于将消息分派给合适的消费者。 这称为复合消息消费者,这是您将在 DI 容器中注册的IMessageConsumer的实现,它将被注入所有需要消息消费者才能开展业务的类中

public class CompositeMessageConsumer : IMessageConsumer 
{
  private readonly IMessageConsumer[] _consumers;

  public CompositeMessageConsumer(IEnumerable<IMessageConsumer> consumers)
  {
    if (consumers is null)
        throw new ArgumentNullException(nameof(consumers));

    this._consumers = consumers.ToArray();
  }

  public bool CanHandleMessage(Message message)
  {
    if (message is null) throw new ArgumentNullException(nameof(message));

    return this._consumers.Any(c => c.CanHandleMessage(message));
  }

  public async Task HandleMessage(Message message)
  {
    if (message is null) 
        throw new ArgumentNullException(nameof(message));

    if (!this.CanHandleMessage(message)) 
        throw new InvalidOperationException("None of the available consumers is able to handle the provided message.");

    var consumer = this._consumers.First(c => c.CanHandleMessage(message));
    await consumer.HandleMessage(message).ConfigureAwait(false);
  }
}

这是使用IMessageConsumer接口的 class 的示例。 在运行时,DI 容器将注入CompositeMessageConsumer的实例。

// this is an example of a class depending on the IMessageConsumer service
public class MessageProcessor 
{
  // at runtime this will be an instance of CompositeMessageConsumer
  private readonly IMessageConsumer _consumer;

  // the DI container will inject an instance of CompositeMessageConsumer here
  public MessageProcessor(IMessageConsumer consumer) 
  {
    if (consumer is null) throw new ArgumentNullException(nameof(consumer));

    this._consumer = consumer;
  }

  public async Task ProcessIncomingMessage(Message message)
  {
    if (message is null) throw new ArgumentNullException(nameof(message));

    // do all the pre processing here...

    // handle the message
    await this._consumer.HandleMessage(message).ConfigureAwait(false);

    // do all the post processing here...
  }
}

如何在 .NET 核心 DI 容器上注册服务

为您的注册确定合适的生命周期是一个超出本讨论的 scope 的问题。

在上面的示例代码中,我定义了无状态消费者类,复合消费者只迭代可用消费者的数组。 数组在迭代期间永远不会被修改。 这意味着所有涉及的类都是线程安全的,因此我们可以使用 singleton 生命周期注册所有这些类。

也就是说,您可以执行的最简单的注册如下:

// register the consumers as classes
services.AddSingleton<FooMessageConsumer>();
service.AddSingleton<BarMessageConsumer>();

// register the composite message consumer as an interface, so that when you require IMessageConsumer you get CompositeMessageConsumer
services.AddSingleton<IMessageConsumer>(container => 
{
    var fooConsumer = container.GetRequiredService<FooMessageConsumer>();
    var barConsumer = container.GetRequiredService<BarMessageConsumer>();

    return new CompositeMessageConsumer(new IMessageConsumer[] 
    {
        fooConsumer,
        barConsumer
    });
});

这是一本了解这些主题的好书。 如果您是 .NET 开发人员,这绝对是必读的。

尝试这个:

private readonly IEnumerable<ICompleteConsumer> completeConsumers;

 public ConsumerHandlerService(IEnumerable<ICompleteConsumer> completeConsumers) {
  this.completeConsumers = completeConsumer
 }


...

//Get the service you want basing on the messagetype, I guess
var completeConsumer = this.completeConsumers.First(c => c.Type == MessageType); // adjust this to your needs

 switch (MessageType) {
  case (MessageType .1) //I want SampleClass1 implementation  to here
  var model = JsonConvert.DeserializeObject < Model > (message);
  await completeConsumer.complete(model);
  break;

  case (MessageType .2) // I want SampleClass2 implementation to here
  var model = JsonConvert.DeserializeObject < Model > (message);
  await completeConsumer.complete(model);
  break;
 }

您可以通过 IEnumerable 获得多项服务

public class ConsumerHandlerService
{
    private readonly IEnumerable<ICompleteConsumer> _completeConsumers;

    public ConsumerHandlerService(IEnumerable<ICompleteConsumer> completeConsumers)
    {
        this.completeConsumers = completeConsumers;
    }

    public void DoSomeWork(string message)
    {
        var model = JsonConvert.DeserializeObject<Model>(message);
        foreach (var completeConsumer in completeConsumers)
        {
            completeConsumer.Complete(model);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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