web api中的依赖注入

Posted

技术标签:

【中文标题】web api中的依赖注入【英文标题】:Dependency injection in web api 【发布时间】:2019-04-05 00:15:57 【问题描述】:

我有一个关于构建依赖关系的正确方法的问题。

假设你有一个 web api 控制器,它有一个带有两个参数的方法:id 和 bar。 控制器需要根据 'id' 值读取特定配置,并向应用了该配置的不同服务发出请求。

    public class FooController : ApiController
    
        private readonly IConfigurationProvider _configurationProvider;
        private readonly IService _service;

        public FooController(IConfigurationProvider configurationProvider, IService service)
       
             _configurationProvider = configurationProvider;
             _service = service;
       

        public IHttpActionResult Bar(int id, int bar)
        
            var configuration = _configurationProvider.GetConfiguration(id);
            _service.Configure(configuration);
            var barResult = _service.Bar(bar);

            return Ok(barResult);
        

我不太确定我在依赖注入方面做得对:

服务在构造函数中初始化,但配置 在执行请求之前不知道。服务的客户 可能不会调用配置,它可能会导致问题(会某种 Builder 模式在这里工作?)。 服务本身是否应该负责其配置加载?

从依赖注入的角度来看,这个例子还有其他问题吗?

谢谢。

【问题讨论】:

您的依赖结构似乎有误。控制器依赖于 IConfigurationProvider,但实际上不做任何事情,只是将检索到的配置传递给它也依赖的服务。此外,依赖项不应该是基础设施代码,IConfigurationProvider 看起来就像基础设施代码渗入您的控制器。所以真正应该发生的是,这些基础设施问题应该在您的组件注册代码中处理。 了解您正在使用的 IoC 容器也很好。 【参考方案1】:

您可以尝试使用工厂类(构建器类可能是错误的)

public class ServiceFactory : IServiceFactory

    private readonly Dictionary<int, IService> _services = new Dictionary<int, IService>();
    private readonly IConfigurationProvider _configurationProvider;

    public ServiceFactory(IConfigurationProvider configurationProvider)
    
        _configurationProvider = configurationProvider;
    

    public IService GetService(int id)
    
        if (!_services.ContainsKey(id))
        
            var config = _configurationProvider.GetConfiguration(id);
            var service = new Service(config);
            _services.Add(id, service);
        

        return _services[id];
    

这将创建一个服务实例,并在工厂类的生命周期内保持对它的引用,因此它只需要创建一次。然后注册 ServiceFactory 并将其注入到你的控制器中。

public class FooController : ApiController

    private readonly IServiceFactory _serviceFactory;

    public FooController(IServiceFactory serviceFactory)
    
        _serviceFactory = serviceFactory;
    

    public IHttpActionResult Bar(int id, int bar)
    
        var service = _serviceFactory.GetService(id);
        var barResult = service.Bar(bar);

        return Ok(barResult);
    

现在你的控制器仍然是可测试的,你不必担心 Configure() 被调用,因为 'id' 是对服务构造函数的依赖。

附:不尊重@Ahmed Sherien,但不要使用 Unity,它又旧又慢!!! ;-)

【讨论】:

【参考方案2】:

在这些情况下,我会使用键控解析...如果您使用统一,则类似这样:

IUnityContainer container = new UnityContainer();
container.RegisterType<IConfiguration, ConfigurationType1>("Type1");
container.RegisterType<IConfiguration, ConfigurationType2>("Type2");


IConfiguration cfg1 = container.Resolve<IConfiguration>("Type1");  // return ConfigurationType1 object
IConfiguration cfg2 = container.Resolve<IConfiguration>("Type2"); // return ConfigurationType2 object

在您的情况下,实现将如下所示:

public class FooController : ApiController

    private readonly IService _service;

    public FooController(IService service)
    
        _service = service;
    

    public IHttpActionResult Bar(int id, int bar)
    
        var configuration = container.Resolve<IConfiguration>(id.ToString());
        _service.Configure(configuration);
        var barResult = _service.Bar(bar);

        return Ok(barResult);
    

【讨论】:

直接在控制器内部使用容器可能是个坏主意,首先现在你的控制器是不可测试的(如果你想测试你的控制器)它实际上让我想起了服务定位器模式太可怕了,其次,您的代码现在可以与您选择的 IoC 容器紧密耦合。在您的代码示例中,“容器”变量来自哪里? @matt_lethargic 我会在全局变量中定义它,但显然这也是个坏主意...... 是的,全局变量仍然很糟糕,仍然使您的控制器无法测试

以上是关于web api中的依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

用于依赖注入的 MVC Web API 和 Unity

.NET Core Web API使用依赖注入(DI)进行服务配置一

web API .net - .net core 对比学习-依赖注入

Simple Injector 无法在 Web API 控制器中注入依赖项

无法使用 Unity 将依赖项注入 ASP.NET Web API 控制器

在 .NET Web Api 的控制器中捕获依赖注入错误