Bud*_*lum 3 c# dependency-injection asp.net-web-api asp.net-core
我有一个名为 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
}
}
Run Code Online (Sandbox Code Playgroud)
我像这样在启动中注册了这些
services.AddScoped<ICompleteConsumer, SampleClass1>(),
services.AddScoped<ICompleteConsumer, SampleClass2>(),
Run Code Online (Sandbox Code Playgroud)
我注入这些依赖项如下
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用 .net core DI 实现此目的?
你的问题其实有两个值得商榷的地方。一是与代码设计相关的点,另一点是与如何使用 .NET core DI 容器来处理所需注册相关的点。两者都很重要,但我们需要一次只对待其中一个。
为了以干净且可扩展的方式解决您的问题,您需要使用称为复合设计模式的设计模式。为此,您需要将接口的定义更改为以下内容:
public interface IMessageConsumer
{
bool CanHandleMessage(Message message);
Task HandleMessage(Message message);
}
Run Code Online (Sandbox Code Playgroud)
然后,您的接口实现将更改如下:
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)}");
}
}
Run Code Online (Sandbox Code Playgroud)
此时您需要引入一个特殊的消息消费者,它将用于将消息分派给正确的消费者。IMessageConsumer这称为复合消息使用者,这是您将在 DI 容器中注册的实现,并将被注入到需要消息使用者来完成其业务的所有类中。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是使用该接口的类的示例IMessageConsumer。在运行时,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...
}
}
Run Code Online (Sandbox Code Playgroud)
确定注册的适当生命周期是一个超出本讨论范围的问题。
在上面的示例代码中,我定义了无状态消费者类,复合消费者仅迭代可用消费者的数组。该数组在迭代过程中永远不会被修改。这意味着所有涉及的类都是线程安全的,因此我们可以使用单例生命周期注册所有它们。
也就是说,您可以执行的最简单的注册如下:
// 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
});
});
Run Code Online (Sandbox Code Playgroud)
这本书是一本了解这些主题的好书。如果您是一名 .NET 开发人员,这绝对是必读的一本书。
| 归档时间: |
|
| 查看次数: |
3372 次 |
| 最近记录: |