从静态工厂访问 ASP.NET 核心 DI 容器 Class
Accessing ASP.NET Core DI Container From Static Factory Class
我创建了一个 ASP.NET 核心 MVC/WebApi 站点,该站点有一个基于 James Still 博客文章 Real-World PubSub Messaging with RabbitMQ 的 RabbitMQ 订阅者 Real-World PubSub Messaging with RabbitMQ。
在他的文章中,他使用静态 class 来启动队列订阅者并为排队的事件定义事件处理程序。然后,此静态方法通过静态工厂 class.
实例化事件处理程序 classes
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 核心应用程序订阅 RabbitMQ?我找到了 RestBus,但尚未针对核心 1.x
进行更新
以下是我对您的案例的看法:
如果可能,我会将已解析的服务作为参数发送
public static IMessageProcessor Create(string messageType, IIpSetService ipService)
{
//
}
否则使用寿命很重要。
如果服务是单例的,我将只设置对配置方法的依赖:
// 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
}
如果限定服务生命周期,我会使用 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
}
您可以避免静态 classes 并通过结合使用依赖注入:
- 使用
IApplicationLifetime
来start/stop监听器每当应用starts/stops.
- 使用
IServiceProvider
创建消息处理器的实例。
首先,让我们将配置移动到它自己的 class,可以从 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
转换为非静态 class,接收 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");
}
}
}
这样您的消息处理器 classes 可以在构造函数中接收它们需要的任何依赖项(只要您在 Startup.ConfigureServices
中配置它们)。例如,我将 ILogger 注入我的示例处理器之一:
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
的非静态class。和你原来的很像,我只是换了参数具有选项依赖项和处理程序工厂的 Start 方法现在是依赖项而不是静态 class:
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
方法,以便它了解您的服务和选项(如果需要,您可以为侦听器和处理程序工厂创建接口):
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
参数和 start/stop 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();
});
// ...
}
尽管使用依赖注入是更好的解决方案,但在某些情况下您必须使用静态方法(例如在扩展方法中)。
对于这些情况,您可以将静态 属性 添加到静态 class 并在 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;
}
}
并在您的 ConfigureServices 中:
EnumExtentions.StringLocalizerFactory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
我知道我的回答晚了,但我想分享我是如何做到的。
首先:使用ServiceLocator是Antipattern所以尽量不要用。
在我的例子中,我需要它来调用 MediatR inside of my DomainModel to implement the DomainEvents 逻辑。
但是,我必须找到一种方法在我的 DomainModel 中调用静态 class 以从 DI 获取某些已注册服务的实例。
所以我决定使用 HttpContext
来访问 IServiceProvider
但我需要从静态方法访问它而不在我的域模型中提及它。
让我们开始吧:
1- 我创建了一个接口来包装 IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- 然后我创建了一个静态 class 作为我的 ServiceLocator 访问点
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;
}
}
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);
}
}
4- 我应该像这样在 DI 中注册 IServiceProviderProxy
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- 最后一步是在应用程序启动时用 IServiceProviderProxy
的实例初始化 ServiceLocator
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
因此,现在您可以在您的 DomainModel classes "Or and needed place" 中调用 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));
}
}
您可以在Configure
中获取服务参考:
app.UseMvc();
var myServiceRef = app.ApplicationServices.GetService<MyService>();
然后将其传递给初始化函数或在 class
上设置静态成员
当然,如其他答案中所述,依赖注入将是更好的解决方案...
Here 是 ServiceLocator 的一个很好的实现,它也使用 Scope。所以甚至适用于 IHttpContextAccessor!
只需将 this class 复制到您的代码中。然后注册ServiceLocator
ServiceActivator.Configure(app.ApplicationServices);
重要说明:ServiceLocator 被视为反模式,因此如果您有任何其他选择,请不要使用它!!!!
我创建了一个 ASP.NET 核心 MVC/WebApi 站点,该站点有一个基于 James Still 博客文章 Real-World PubSub Messaging with RabbitMQ 的 RabbitMQ 订阅者 Real-World PubSub Messaging with RabbitMQ。
在他的文章中,他使用静态 class 来启动队列订阅者并为排队的事件定义事件处理程序。然后,此静态方法通过静态工厂 class.
实例化事件处理程序 classesusing 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 核心应用程序订阅 RabbitMQ?我找到了 RestBus,但尚未针对核心 1.x
进行更新以下是我对您的案例的看法:
如果可能,我会将已解析的服务作为参数发送
public static IMessageProcessor Create(string messageType, IIpSetService ipService)
{
//
}
否则使用寿命很重要。
如果服务是单例的,我将只设置对配置方法的依赖:
// 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
}
如果限定服务生命周期,我会使用 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
}
您可以避免静态 classes 并通过结合使用依赖注入:
- 使用
IApplicationLifetime
来start/stop监听器每当应用starts/stops. - 使用
IServiceProvider
创建消息处理器的实例。
首先,让我们将配置移动到它自己的 class,可以从 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
转换为非静态 class,接收 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");
}
}
}
这样您的消息处理器 classes 可以在构造函数中接收它们需要的任何依赖项(只要您在 Startup.ConfigureServices
中配置它们)。例如,我将 ILogger 注入我的示例处理器之一:
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
的非静态class。和你原来的很像,我只是换了参数具有选项依赖项和处理程序工厂的 Start 方法现在是依赖项而不是静态 class:
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
方法,以便它了解您的服务和选项(如果需要,您可以为侦听器和处理程序工厂创建接口):
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
参数和 start/stop 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();
});
// ...
}
尽管使用依赖注入是更好的解决方案,但在某些情况下您必须使用静态方法(例如在扩展方法中)。
对于这些情况,您可以将静态 属性 添加到静态 class 并在 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;
}
}
并在您的 ConfigureServices 中:
EnumExtentions.StringLocalizerFactory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
我知道我的回答晚了,但我想分享我是如何做到的。
首先:使用ServiceLocator是Antipattern所以尽量不要用。 在我的例子中,我需要它来调用 MediatR inside of my DomainModel to implement the DomainEvents 逻辑。
但是,我必须找到一种方法在我的 DomainModel 中调用静态 class 以从 DI 获取某些已注册服务的实例。
所以我决定使用 HttpContext
来访问 IServiceProvider
但我需要从静态方法访问它而不在我的域模型中提及它。
让我们开始吧:
1- 我创建了一个接口来包装 IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- 然后我创建了一个静态 class 作为我的 ServiceLocator 访问点
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;
}
}
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);
}
}
4- 我应该像这样在 DI 中注册 IServiceProviderProxy
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- 最后一步是在应用程序启动时用 IServiceProviderProxy
的实例初始化 ServiceLocator
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
因此,现在您可以在您的 DomainModel classes "Or and needed place" 中调用 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));
}
}
您可以在Configure
中获取服务参考:
app.UseMvc();
var myServiceRef = app.ApplicationServices.GetService<MyService>();
然后将其传递给初始化函数或在 class
上设置静态成员当然,如其他答案中所述,依赖注入将是更好的解决方案...
Here 是 ServiceLocator 的一个很好的实现,它也使用 Scope。所以甚至适用于 IHttpContextAccessor!
只需将 this class 复制到您的代码中。然后注册ServiceLocator
ServiceActivator.Configure(app.ApplicationServices);
重要说明:ServiceLocator 被视为反模式,因此如果您有任何其他选择,请不要使用它!!!!