.NET Core DI 调查 IConfiguration

.NET Core DI looking into IConfiguration

在 ASP.NET Core DI 中支持基于属性的设置引用解析的最简单方法是什么?我想做这样的事情来读取配置值:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    public WeatherForecastController(
        [Setting("Logging:LogLevel:Microsoft")] string logLevel)
    {
        …

appsettings.json 看起来像:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      …

有什么办法吗?我希望它也能用于服务。

从技术上讲,可以使用 MS.DI 实现此目的,但这样做非常困难,而且正如 Dmitry 所提到的,使用成熟的 DI 容器(例如 Autofac 或 Simple Injector)会更幸运。

如果您真的想继续使用 MS.DI,这里有一些代码可以帮助您入门:

var settingDescriptors = (
    from descriptor in services
    let type = descriptor.ImplementationType
    where type != null && !type.IsGenericTypeDefinition
    where type.GetConstructors().Single().GetParameters()
        .Any(p => p.GetCustomAttribute<SettingAttribute>() != null)
    select descriptor)
    .ToArray();

foreach (var descriptor in settingDescriptors)
{
    services.Remove(descriptor);

    object[] settings = // TODO: Determine settings from parameters here

    services.Add(new ServiceDescriptor(
        descriptor.ServiceType,
        c => ActivatorUtilities.CreateInstance(
            c,
            descriptor.ImplementationType,
            settings),
        descriptor.Lifetime));
}

此代码的作用是遍历服务集合并替换具有构造函数参数的实现的所有注册,该参数标有 SettingAttribute。该注册被替换为使用工厂委托的注册,该委托要求 DI 容器创建一个实例,同时向其提供一个或多个对象(设置)。换句话说,它在手动提供对象的同时启用自动装配。

这意味着,在某种程度上,MS.DI 可以做到这一点,但是......您应该考虑一些严重的限制:

  • 此代码不适用于开放通用注册。事实上,正是 MS.DI 的限制阻止了您将此方法有效地应用于开放泛型类型。
  • 一旦应用,您就不能应用其他以相同方式工作的扩展,因为一旦您将实现类型的描述符替换为使用工厂委托的描述符,DI 系统就会变得盲目。例如,如果您使用 Scrutor(在 MS.DI 之上工作)应用装饰器,您可能会看到一些奇怪的行为,具体取决于您应用它们的顺序。
  • ActivatorUtilities.CreateInstance 的使用使 DI 系统失明。 MS.DI 包含防止循环依赖的检查,使用 ActivatorUtilities.CreateInstance 将导致在循环依赖的情况下难以调试堆栈溢出异常。
  • 这个特定的实现是幼稚的,并且可能会搞砸,因为它改变了注册的顺序。如果您使用作为集合一部分的 类 上的属性,或者当您有一个类型替换另一个类型(通常通过将注册附加到列表来工作)时,这可能会中断。要解决此问题,您必须将新描述符插入列表中与删除旧描述符的位置相同的位置。
  • 可能还有更多注意事项,但这些是我能从脑海中回想起的。