gpt4 book ai didi

c# - 从静态工厂类访问 ASP.NET Core DI 容器

转载 作者:IT王子 更新时间:2023-10-29 04:18:41 26 4
gpt4 key购买 nike

我创建了一个 ASP.NET Core MVC/WebApi 网站,该网站有一个基于 James Still 博客文章的 RabbitMQ 订阅者 Real-World PubSub Messaging with RabbitMQ .

在他的文章中,他使用静态类来启动队列订阅者并为排队的事件定义事件处理程序。然后,此静态方法通过静态工厂类实例化事件处理程序类。

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace NST.Web.MessageProcessing
{
public static class MessageListener
{
private static IConnection _connection;
private static IModel _channel;

public static void Start(string hostName, string userName, string password, int port)
{
var factory = new ConnectionFactory
{
HostName = hostName,
Port = port,
UserName = userName,
Password = password,
VirtualHost = "/",
AutomaticRecoveryEnabled = true,
NetworkRecoveryInterval = TimeSpan.FromSeconds(15)
};

_connection = factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.ExchangeDeclare(exchange: "myExchange", type: "direct", durable: true);

var queueName = "myQueue";

QueueDeclareOk ok = _channel.QueueDeclare(queueName, true, false, false, null);

_channel.QueueBind(queue: queueName, exchange: "myExchange", routingKey: "myRoutingKey");

var consumer = new EventingBasicConsumer(_channel);
consumer.Received += ConsumerOnReceived;

_channel.BasicConsume(queue: queueName, noAck: false, consumer: consumer);

}

public static void Stop()
{
_channel.Close(200, "Goodbye");
_connection.Close();
}

private static void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea)
{
// get the details from the event
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var messageType = "endpoint"; // hardcoding the message type while we dev...

// instantiate the appropriate handler based on the message type
IMessageProcessor processor = MessageHandlerFactory.Create(messageType);
processor.Process(message);

// Ack the event on the queue
IBasicConsumer consumer = (IBasicConsumer)sender;
consumer.Model.BasicAck(ea.DeliveryTag, false);
}

}
}

它工作得很好,直到我现在需要在我的消息处理器工厂中解析服务,而不是仅仅写入控制台。

using NST.Web.Services;
using System;

namespace NST.Web.MessageProcessing
{
public static class MessageHandlerFactory
{
public static IMessageProcessor Create(string messageType)
{
switch (messageType.ToLower())
{
case "ipset":
// need to resolve IIpSetService here...
IIpSetService ipService = ???????

return new IpSetMessageProcessor(ipService);

case "endpoint":
// need to resolve IEndpointService here...
IEndpointService epService = ???????

// create new message processor
return new EndpointMessageProcessor(epService);

default:
throw new Exception("Unknown message type");
}
}
}
}

有什么方法可以访问 ASP.NET Core IoC 容器来解决依赖关系?我真的不想手动启动整个依赖项堆栈 :(

或者,是否有更好的方法从 ASP.NET Core 应用程序订阅 RabbitMQ?我找到了 RestBus但它还没有针对 Core 1.x 进行更新

最佳答案

您可以避免使用静态类并通过结合以下方式使用依赖注入(inject):

  • 使用 IApplicationLifetime 每当应用程序启动/停止时启动/停止监听器。
  • 使用IServiceProvider创建消息处理器的实例。

首先,让我们将配置移动到它自己的类中,该类可以从 appsettings.json 中填充:

public class RabbitOptions
{
public string HostName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int Port { get; set; }
}

// In appsettings.json:
{
"Rabbit": {
"hostName": "192.168.99.100",
"username": "guest",
"password": "guest",
"port": 5672
}
}

接下来,转换MessageHandlerFactory进入接收 IServiceProvider 的非静态类作为依赖。它将使用服务提供者来解析消息处理器实例:

public class MessageHandlerFactory
{
private readonly IServiceProvider services;
public MessageHandlerFactory(IServiceProvider services)
{
this.services = services;
}

public IMessageProcessor Create(string messageType)
{
switch (messageType.ToLower())
{
case "ipset":
return services.GetService<IpSetMessageProcessor>();
case "endpoint":
return services.GetService<EndpointMessageProcessor>();
default:
throw new Exception("Unknown message type");
}
}
}

通过这种方式,您的消息处理器类可以在构造函数中接收它们需要的任何依赖项(只要您在 Startup.ConfigureServices 中配置它们)。例如,我将一个 ILogger 注入(inject)我的示例处理器之一:

public class IpSetMessageProcessor : IMessageProcessor
{
private ILogger<IpSetMessageProcessor> logger;
public IpSetMessageProcessor(ILogger<IpSetMessageProcessor> logger)
{
this.logger = logger;
}

public void Process(string message)
{
logger.LogInformation("Received message: {0}", message);
}
}

现在转换MessageListener进入依赖于 IOptions<RabbitOptions> 的非静态类和 MessageHandlerFactory .它与您原来的非常相似,我只是用选项依赖项替换了 Start 方法的参数,并且处理程序工厂现在是一个依赖项而不是静态类:

public class MessageListener
{
private readonly RabbitOptions opts;
private readonly MessageHandlerFactory handlerFactory;
private IConnection _connection;
private IModel _channel;

public MessageListener(IOptions<RabbitOptions> opts, MessageHandlerFactory handlerFactory)
{
this.opts = opts.Value;
this.handlerFactory = handlerFactory;
}

public void Start()
{
var factory = new ConnectionFactory
{
HostName = opts.HostName,
Port = opts.Port,
UserName = opts.UserName,
Password = opts.Password,
VirtualHost = "/",
AutomaticRecoveryEnabled = true,
NetworkRecoveryInterval = TimeSpan.FromSeconds(15)
};

_connection = factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.ExchangeDeclare(exchange: "myExchange", type: "direct", durable: true);

var queueName = "myQueue";

QueueDeclareOk ok = _channel.QueueDeclare(queueName, true, false, false, null);

_channel.QueueBind(queue: queueName, exchange: "myExchange", routingKey: "myRoutingKey");

var consumer = new EventingBasicConsumer(_channel);
consumer.Received += ConsumerOnReceived;

_channel.BasicConsume(queue: queueName, noAck: false, consumer: consumer);

}

public void Stop()
{
_channel.Close(200, "Goodbye");
_connection.Close();
}

private void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea)
{
// get the details from the event
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var messageType = "endpoint"; // hardcoding the message type while we dev...
//var messageType = Encoding.UTF8.GetString(ea.BasicProperties.Headers["message-type"] as byte[]);

// instantiate the appropriate handler based on the message type
IMessageProcessor processor = handlerFactory.Create(messageType);
processor.Process(message);

// Ack the event on the queue
IBasicConsumer consumer = (IBasicConsumer)sender;
consumer.Model.BasicAck(ea.DeliveryTag, false);
}
}

差不多了,您需要更新 Startup.ConfigureServices方法,以便它了解您的服务和选项(如果需要,您可以为监听器和处理程序工厂创建接口(interface)):

public void ConfigureServices(IServiceCollection services)
{
// ...

// Add RabbitMQ services
services.Configure<RabbitOptions>(Configuration.GetSection("rabbit"));
services.AddTransient<MessageListener>();
services.AddTransient<MessageHandlerFactory>();
services.AddTransient<IpSetMessageProcessor>();
services.AddTransient<EndpointMessageProcessor>();
}

最后,更新 Startup.Configure采取额外的方法IApplicationLifetime参数并启动/停止 ApplicationStarted 中的消息监听器/ApplicationStopped事件(尽管我不久前注意到使用 IISExpress 的 ApplicationStopping 事件存在一些问题,如 this question 中所示):

public MessageListener MessageListener { get; private set; }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
appLifetime.ApplicationStarted.Register(() =>
{
MessageListener = app.ApplicationServices.GetService<MessageListener>();
MessageListener.Start();
});
appLifetime.ApplicationStopping.Register(() =>
{
MessageListener.Stop();
});

// ...
}

关于c# - 从静态工厂类访问 ASP.NET Core DI 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40611683/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com