【问题标题】:ILogger and DependencyInjection Architecture in ASP.NET CoreASP.NET Core 中的 ILogger 和 DependencyInjection 架构
【发布时间】:2021-05-18 12:16:52
【问题描述】:

我的团队非常依赖 DependencyInjection。就个人而言,我最近有点离题太远了,无法真正判断这个的正确用法。但我确实看到越来越多这样的代码:

public AuthenticationApi(ILogger<AuthenticationApi> logger,
                         HttpClient httpClient,
                         IJsonConverter jsonConverter,
                         IDtoConverter dtoConverter) : base(logger, httpClient, jsonConverter)
{
    _dtoConverter = dtoConverter;
}

然后这会在代码中成倍增加,我们一半的代码只是在无限调用构造函数 DependencyInjection 相关的东西。我的团队告诉我,这就是 .NET Core 的方式。是的,这样的答案证实了这一点:

ILogger and DependencyInjection in ASP.NET Core 2+

这样的讨论更符合我的直觉,即日志记录等事情应该是透明的,而不是在无休止的 DependencyInjection 构造函数链中处理:

https://softwareengineering.stackexchange.com/questions/371722/criticism-and-disadvantages-of-dependency-injection

在另一个地方(不幸的是,我再也找不到这篇文章了),我读到这个构造函数问题主要是由于服务工厂实现不当造成的。

赞赏有关该主题的想法。

根据下面的讨论,这是基类,同时使用 Logger 和 HttpClient:

internal class ApiBase
{
    private readonly ILogger _logger;
    private readonly IJsonConverter _jsonConverter;
    private readonly HttpClient _httpClient;

    public ApiBase(ILogger logger, HttpClient httpClient, IJsonConverter jsonConverter)
    {
        _logger = logger;
        _jsonConverter = jsonConverter;
        _httpClient = httpClient;
    }

    protected async Task<T> GetAsync<T>(string path, HttpContent content = null)
    {
        _logger.LogDebug($"Sending GET request to {path}");

        using (var request = new HttpRequestMessage(HttpMethod.Get, path))
        {
            request.Content = content;

            using (var response = await _httpClient.SendAsync(request).ConfigureAwait(false))
            {
                if (response.IsSuccessStatusCode)
                {
                    _logger.LogDebug($"GET request to {path} was successful.");

                    var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    var deserializeResponseContent = _jsonConverter.Deserialize<T>(responseContent);

                    return deserializeResponseContent;
                }

                var message = GetErrorMessage("GET", path, response);
                _logger.LogError(message);
                throw new HttpRequestException(message);
            }
        }
    }

【问题讨论】:

  • 不,这根本不是 .NET Core 的方式。也没有涉及到服务工厂——那些构造函数对它们的参数是如何创建的一无所知。无论您使用构造函数或参数注入,还是根本不使用注入,您总会遇到方法参数过多的情况。典型的解决方案是创建一个参数对象,结合常见的依赖关系。
  • 至于HttpClient,除非您在 Blazor WASM 中编写 SPA,否则您可能永远不会只使用 HttpClient。在任何其他情况下,您将需要调用多个远程服务,因此您将拥有具有自己的 cookie、身份验证、重试策略的类型化类,而不是单个 HttpClient。一些依赖关系非常普遍,因此创建一个 context 类来组合它们并将其传递到任何地方可能是有意义的。
  • 至于其他两个转换器……嗯?这些东西是干什么用的?您是否打算即时从 System.Text.Json 切换到 Json.NET?那有什么意义呢?如果你想使用 AutoMapper,你可以注入一个 IMapper 实例,而不是把它包装在另一个接口中,以防你决定切换映射库
  • 那么,我猜你是作者之一,@Steven?
  • @Fildor:是的,我认为这很清楚 ;-)

标签: c# .net-core dependency-injection


【解决方案1】:

然后这会在代码中成倍增加,其中一半的代码只是调用具有无穷无尽的 DependencyInjection 相关内容的构造函数。我的团队告诉我,这就是 .NET Core 的方式。

是和不是。依赖项的构造函数注入是 .NET Core 中用于组织依赖项的标准方法。而且效果很好。

什么是非标准的是你的基类,事实上你有那些构造函数链,其中一半参数实际上并不需要,而只是进入基类构造函数。我敢打赌,这个基类实际上并没有做任何有价值的事情。

删除基类。查看每个控制器仍然需要什么。只注入那个。这些基类和它们自己的构造函数是混淆实际依赖关系的好方法。因为现在突然每个类都需要一个IJsonConverter,一定很重要。但是您将很难弄清楚谁实际上使用了使用它的基类功能。那么在您从基础派生的 20 个类中,谁真正需要它,谁只需要它来使编译器满意?

我的建议是删除基类。向每个控制器注入他们需要的东西,不多也不少。所以你实际上可以看到依赖关系。如果您在该基类中具有通用功能,则它可能是某个将这些字段作为参数的静态方法。或者它可以是它自己的服务,在需要的地方注入。但在需要的地方。

【讨论】:

  • 我已经添加了基类。我们必须从这段代码中调用的 API 非常非正统,并且不像通常的 REST API 那样工作。这可能是所有这一切的原因。
  • 我认为这里不需要基类。让它成为另一种服务。在需要的地方注入服务。完毕。没有更多的构造函数链。
猜你喜欢
  • 2019-02-07
  • 1970-01-01
  • 2021-11-23
  • 2021-07-09
  • 2018-02-09
  • 2021-12-17
  • 2015-12-12
  • 2018-03-13
  • 2020-08-08
相关资源
最近更新 更多