在项目之间共享Microsoft.Extensions.DependencyInjection.ServiceProvider配置

Jos*_*osh 1 c# dependency-injection .net-core asp.net-core

我有一个包含以下项目的解决方案

  • Acme.Core
  • Acme.Domain
  • Acme资料库
  • Acme.Services
  • Acme.Web

过去,我在完整框架项目中使用Unity for DI。我能够将具体对象注册到可执行项目(Web应用程序,控制台应用程序,测试应用程序)中的接口映射。

我正在尝试使用.NET Core实现相同的方法。我想首先尝试使用Microsoft.Extensions.DependencyInjection库。在ASP.NET Core应用程序中,它运行良好。不幸的是,当我尝试与注册共享/引用该实例到其他项目(例如.NET Standard库)时,遇到了一个问题。

我的想法是将ServiceProvider注入服务的构造函数中:

public class AddressService : BaseService, IAddressService
{
        private readonly IServiceProvider _serviceProvider;

        public AddressService(IServiceProvider serviceProvider, string userOrProcessName)
        {
           _serviceProvider = serviceProvider;
        } 

        public IReadOnlyList<IState> GetAllStates()
        {
            _serviceProvider.GetService<IAddressRepository>();

            // other logic removed
        }
}
Run Code Online (Sandbox Code Playgroud)

我在Startup.ConfigureServices()中尝试了以下方法:

services.AddTransient<IAddressService>(s => new AddressService(HttpContext.RequestServices, Environment.UserName));
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是我无法在Controller外部引用HttpContext.RequestServices。我还没有找到传递ServiceProvider实例的另一种方法。

我的问题:

  1. 如何传递当前ServiceProvider的引用?
  2. 有没有更好的设计可以实现我在多个库中共享Microsoft.Extensions.DependencyInjection的配置的目标?

Ste*_*ven 5

防止注入IServiceProvider您的应用程序组件;导致服务定位器反模式

相反,您应该仅使用Constructor Injection构建应用程序组件。这意味着您AddressService应该要求IAddressRepositoryas作为构造函数参数,而不是IServiceProvider。例如:

public class AddressService : IAddressService
{
    private readonly IAddressRepository repo;

    public AddressService(IAddressRepository repo, IUserContext userContext)
    {
       this.repo = repo;
    } 

    public IReadOnlyList<IState> GetAllStates()
    {
        // other logic removed
    }
}
Run Code Online (Sandbox Code Playgroud)

还要尝试防止将素数注入构造函数中。这本身并不是一个坏习惯,但是它确实使对象图的构造变得复杂。相反,可以将值包装到一个类中(以防它是配置值),或者将其隐藏在一个抽象后面(如上所示),以防它是运行时值。

两种做法都简化了应用程序代码和“ 合成根”

例如,这将是先前AddressService重新设计的结果:

services.AddTransient<IAddressRepository, SqlAddressRepository>();
services.AddTransient<IAddressService, AddressService>();
services.AddScoped<IUserContext, UserContext>();
services.AddHttpContextAccessor();
Run Code Online (Sandbox Code Playgroud)

在这里,UserContext可以定义如下:

public class UserContext : IUserContext
{
    private readonly IHttpContextAccessor accessor;
    public UserContext(IHttpContextAccessor accessor) => this.accessor = accessor;
    public string UserName => this.accessor.HttpContext.User.Identity.Name;
}
Run Code Online (Sandbox Code Playgroud)