Accessing ASP.NET Core DI Container From Static Factory Class

后端 未结 5 1610
闹比i
闹比i 2021-01-30 16:57

I\'ve created an ASP.NET Core MVC/WebApi site that has a RabbitMQ subscriber based off James Still\'s blog article Real-World PubSub Messaging with RabbitMQ.

In his arti

5条回答
  •  心在旅途
    2021-01-30 17:35

    I know my answer is late, but I wanted to share how I did it.

    First of all: It's Antipattern to use ServiceLocator so try not to use it as you can. In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.

    However, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.

    So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.

    Let's do it:

    1- I've created an interface to wrap the IServiceProvider

    public interface IServiceProviderProxy
    {
        T GetService();
        IEnumerable GetServices();
        object GetService(Type type);
        IEnumerable GetServices(Type type);
    }
    
    
    

    2- Then I've created a static class to be my ServiceLocator access point

    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- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor

    public class HttpContextServiceProviderProxy : IServiceProviderProxy
    {
        private readonly IHttpContextAccessor contextAccessor;
    
        public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
        {
            this.contextAccessor = contextAccessor;
        }
    
        public T GetService()
        {
            return contextAccessor.HttpContext.RequestServices.GetService();
        }
    
        public IEnumerable GetServices()
        {
            return contextAccessor.HttpContext.RequestServices.GetServices();
        }
    
        public object GetService(Type type)
        {
            return contextAccessor.HttpContext.RequestServices.GetService(type);
        }
    
        public IEnumerable GetServices(Type type)
        {
            return contextAccessor.HttpContext.RequestServices.GetServices(type);
        }
    }
    
    
    

    4- I should register the IServiceProviderProxy in the DI like this

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
        services.AddSingleton();
        .......
    }
    

    5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup

    public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
    {
        ServiceLocator.Initialize(sp.GetService());
    }
    

    As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.

    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();
            await mediator.Send(new FakeModelUpdated(this));
        }
    }
    

    提交回复
    热议问题