Как получить тип, в который вводится зависимость? - PullRequest
1 голос
/ 05 марта 2020

Я работаю с. net core 2.2 и использую внедрение зависимостей. У меня есть некоторые пользовательские зависимости инфраструктуры, которые управляют ведением журнала, трассировкой и т. Д. c. что я впрыскиваю в классы. Мне бы хотелось, чтобы зависимости знали, в каком классе они находятся. Я могу это сделать, если создаю экземпляр класса, например new Logger<Type>();, но возможно ли это с помощью внедрения зависимостей?

Пример:

public class Logger<Type> : ILogger
{

}

public class Foo
{
    private Ilogger _logger;

    public Foo(ILogger logger)
    {
       _logger = logger;
    }

    public void DoStuff()
    {
        _logger.Log();  //<= knows it's in the Foo class.
    }
}

Как мне ввести ILogger в Foo, и чтобы Logger знал, в какой тип он был введен?

Ответы [ 2 ]

1 голос
/ 06 марта 2020

Хотя более зрелые и многофункциональные DI-контейнеры, такие как Autofa c и Simple Injector , поддерживают инъекцию на основе контекста (это функция, которую вы ищете) MS.DI не поддерживает эту функцию.

Именно по этой причине во всей документации Microsoft описывается внедрение обобщенного c ILogger<T>, где T равен типу потребления, как в :

public class C
{
    public C(ILogger<C> logger) { ... }
}

Разрешение потребителю зависеть от универсального c ILogger<T>, а не просто зависеть от универсального c ILogger, однако более многословно и подвержено ошибкам. Это делает ваш код сложнее для тестирования и сложнее поддерживать. Это, вероятно, причина, по которой вы пытаетесь внедрить контекстно-зависимый ILogger вместо этого, и я приветствую вас за это.

Вы можете «взломать» эту функцию в MS.DI, выполнив итерацию по ServiceCollection, но есть слишком много ограничений, чтобы это работало в вашем производственном приложении. Однако для развлечения и развлечения вы можете попробовать вот что:

// Run just before finalizing the IServiceCollection
for (int i = 0; i < services.Count; i++)
{
    ServiceDescriptor descriptor = services[i];

    if (descriptor.ImplementationType != null)
    {
        var loggerParameters =
            from ctor in descriptor.ImplementationType.GetConstructors()
            from param in ctor.GetParameters()
            where param.ParameterType == typeof(ILogger)
            select param;

        if (loggerParameters.Any())
        {
            // Replace registration
            services[i] =
                new ServiceDescriptor(
                    descriptor.ServiceType,
                    provider =>
                        ActivatorUtilities.CreateInstance(
                            provider,
                            descriptor.ImplementationType,
                            provider.GetRequiredService(
                                typeof(ILogger<>).MakeGenericType(
                                    descriptor.ImplementationType))),
                    descriptor.Lifetime);
        }
    }
}

Этот код выполняет следующие действия: * ищет регистрации * (закрытого или не универсального c) типа реализации *, который использует Auto-Wiring (то есть без регистрации лямбды или регистрации экземпляров) * и конструктор которой зависит от ILogger * и заменяет эту регистрацию регистрацией лямбды, которая имеет тип Auto-Wires (с использованием ActivatorUtilities) при замене зависимости ILogger с зависимостью ILogger<T>.

Ограничения:

  • Не работает с регистрациями open-generi c
  • Не работает с регистрациями делегатов
  • Может не работать при замене MS.DI на другие контейнеры DI (например, может «ослепить» контейнер).
  • Поведение не определено при работе с несколькими конструкторами

In поэтому я бы не советовал использовать этот подход, а вместо этого использовать зрелый DI-контейнер.

СОВЕТ: Чтобы получить вдохновение, здесь - это простой инжектор описание того, как настроить контейнер для внедрения ASP. NET Core's ILogger, и базовое c описание того, как применять контекстную инъекцию в Simple Injector, можно найти здесь .

1 голос
/ 06 марта 2020

Вы можете указать тип, подобный этому

public class Foo
{
    private Ilogger<Foo> _logger;
}

Затем в Logger вы можете узнать тип T по

public class Logger<T> : ILogger<T>
{
    public Type WhatType() => typeof(T);
}

. Вам нужно будет зарегистрировать услугу следующим образом.

serviceCollection.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
...