C# 从静态工厂类访问ASP.NET核心DI容器

C# 从静态工厂类访问ASP.NET核心DI容器,c#,asp.net-core,dependency-injection,rabbitmq,service-locator,C#,Asp.net Core,Dependency Injection,Rabbitmq,Service Locator,我根据James Still的博客文章创建了一个ASP.NET核心MVC/WebApi站点,它有一个RabbitMQ订户 在他的文章中,他使用一个静态类来启动队列订阅者,并为排队事件定义事件处理程序。然后,此静态方法通过静态工厂类实例化事件处理程序类 using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Text; namespace NST.Web.MessageProcessing {

我根据James Still的博客文章创建了一个ASP.NET核心MVC/WebApi站点,它有一个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核心IoC容器来解决依赖关系?我真的不想手动旋转整个依赖项堆栈:(


或者,有没有更好的方法从ASP.NET Core应用程序订阅RabbitMQ?我找到了,但Core 1.x尚未更新。以下是我对您案例的看法:

如果可能,我会将已解析的服务作为参数发送

public static IMessageProcessor Create(string messageType, IIpSetService ipService)
{
    //
}
否则服务寿命将非常重要

若服务是单例的,我只需设置对configure方法的依赖:

 // configure method
public IApplicationBuilder Configure(IApplicationBuilder app)
{
    var ipService = app.ApplicationServices.GetService<IIpSetService>();
    MessageHandlerFactory.IIpSetService = ipService;
}

// static class
public static IIpSetService IpSetService;

public static IMessageProcessor Create(string messageType)
{
    // use IpSetService
}
//配置方法
公共IApplicationBuilder配置(IApplicationBuilder应用程序)
{
var ipService=app.ApplicationServices.GetService();
MessageHandlerFactory.IIpSetService=ipService;
}
//静态类
公共静态IIpSetService IpSetService;
公共静态IMessageProcessor创建(字符串消息类型)
{
//使用IpSetService
}
如果服务生命周期是限定范围的,我将使用HttpContextAccessor:

//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

public IApplicationBuilder Configure(IApplicationBuilder app)
{
    var httpContextAccessor= app.ApplicationServices.GetService<IHttpContextAccessor>();
    MessageHandlerFactory.HttpContextAccessor = httpContextAccessor;
}

// static class
public static IHttpContextAccessor HttpContextAccessor;

public static IMessageProcessor Create(string messageType)
{
    var ipSetService = HttpContextAccessor.HttpContext.RequestServices.GetService<IIpSetService>();
    // use it
}
//Startup.cs
public void配置服务(IServiceCollection服务)
{
services.AddSingleton();
}
公共IApplicationBuilder配置(IApplicationBuilder应用程序)
{
var httpContextAccessor=app.ApplicationServices.GetService();
MessageHandlerFactory.HttpContextAccessor=HttpContextAccessor;
}
//静态类
公共静态IHttpContextAccessor HttpContextAccessor;
公共静态IMessageProcessor创建(字符串消息类型)
{
var ipSetService=HttpContextAccessor.HttpContext.RequestServices.GetService();
//使用它
}

您可以避免使用静态类,并通过以下方式使用依赖项注入:

  • 每当应用程序启动/停止时,使用启动/停止侦听器
  • 使用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");
        }
    }
}
现在,将
MessageListener
转换为一个非静态类,该类依赖于
IOptions
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.Configure
方法以获取额外的
iaapplicationlifetime
参数,并在
ApplicationStarted
/
ApplicationStopped
事件中启动/停止消息侦听器(尽管我刚才注意到使用IISExpress的ApplicationStopping事件存在一些问题,如中所示):

public MessageListener MessageListener{get;private set;}
public void Configure(IApplicationBuilder应用程序、IHostingEnvironment环境、ILoggerFactory日志工厂、IApplicationLifetime应用程序时间)
{
appLifetime.ApplicationStarted.Register(()=>
{
MessageListener=app.ApplicationServices.GetService();
MessageListener.Start();
});
appLifetime.ApplicationStopping.Register(()=>
{
MessageListener.Stop();
});
// ...
}

尽管使用依赖项注入是更好的解决方案,但在某些情况下必须使用静态方法(如扩展方法)

对于这些情况,您可以向静态类添加静态属性,并在ConfigureServices方法中对其进行初始化

例如:

public static class EnumExtentions
{
    static public IStringLocalizerFactory StringLocalizerFactory { set; get; }

    public static string GetDisplayName(this Enum e)
    {
        var resourceManager = StringLocalizerFactory.Create(e.GetType());
        var key = e.ToString();
        var resourceDisplayName = resourceManager.GetString(key);

        return resourceDisplayName;
    }
}
在您的配置服务中:

EnumExtentions.StringLocalizerFactory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
EnumExtensions.StringLocalizerFactory=services.BuildServiceProvider().GetService();

我知道我的答案晚了,但我想和大家分享一下我是如何回答的

首先:要使用服务定位器,所以尽量不要使用它。 在我的例子中,我需要它调用我的DomainModel内部来实现逻辑

然而,我必须找到一种方法,在我的DomainModel中调用一个静态类,以从DI获取某个已注册服务的实例

因此,我决定使用
HttpContext
访问
IServiceProvider
,但我需要从静态方法访问它,而不必在域模型中提及它

让我们开始吧:

1-我创建了一个接口来包装IServiceProvider

public interface IServiceProviderProxy
{
    T GetService<T>();
    IEnumerable<T> GetServices<T>();
    object GetService(Type type);
    IEnumerable<object> GetServices(Type type);
}
3-我已经为
IServiceProviderProxy
创建了一个实现,它在内部使用
IHttpContextAccessor

public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
    private readonly IHttpContextAccessor contextAccessor;

    public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
    {
        this.contextAccessor = contextAccessor;
    }

    public T GetService<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetService<T>();
    }

    public IEnumerable<T> GetServices<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetServices<T>();
    }

    public object GetService(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetService(type);
    }

    public IEnumerable<object> GetServices(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetServices(type);
    }
}
5-最后一步是在应用程序启动时使用
IServiceProviderProxy
实例初始化
ServiceLocator

public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
    ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
public void配置(IApplicationBuilder应用程序、IHostingEnvironment环境、IServiceProvider sp)
{
初始化(sp.GetService());
}
因此,现在您可以在DomainModel类中调用ServiceLocator“或和所需位置”,并解析所需的依赖项

public class FakeModel
{
    public FakeModel(Guid id, string value)
    {
        Id = id;
        Value = value;
    }

    public Guid Id { get; }
    public string Value { get; private set; }

    public async Task UpdateAsync(string value)
    {
        Value = value;
        var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
        await mediator.Send(new FakeModelUpdated(this));
    }
}
公共类伪造模型
{
公共伪造模型(Guid id,字符串值)
{
Id=Id;
价值=价值;
}
公共Guid Id{get;}
公共字符串值{get;private set;}
公共异步任务UpdateAsync(字符串值)
{
价值=价值;
var mediator=ServiceLocator.ServiceProvider.GetService();
等待调解人。
public static class ServiceLocator
{
    private static IServiceProviderProxy diProxy;

    public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");

    public static void Initialize(IServiceProviderProxy proxy)
    {
        diProxy = proxy;
    }
}
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
    private readonly IHttpContextAccessor contextAccessor;

    public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
    {
        this.contextAccessor = contextAccessor;
    }

    public T GetService<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetService<T>();
    }

    public IEnumerable<T> GetServices<T>()
    {
        return contextAccessor.HttpContext.RequestServices.GetServices<T>();
    }

    public object GetService(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetService(type);
    }

    public IEnumerable<object> GetServices(Type type)
    {
        return contextAccessor.HttpContext.RequestServices.GetServices(type);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
    .......
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
    ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
public class FakeModel
{
    public FakeModel(Guid id, string value)
    {
        Id = id;
        Value = value;
    }

    public Guid Id { get; }
    public string Value { get; private set; }

    public async Task UpdateAsync(string value)
    {
        Value = value;
        var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
        await mediator.Send(new FakeModelUpdated(this));
    }
}
app.UseMvc();
var myServiceRef = app.ApplicationServices.GetService<MyService>();