Autofac - как зарегистрировать тип, используемый в качестве параметра конструктора, для которого требуется разрешающий (конструктор) тип в качестве универсального параметра - PullRequest
0 голосов
/ 13 декабря 2018

У меня есть регистратор типа ILogger.

Для любого типа T, для которого в конструкторе требуется ILogger, экземпляр ILogger создается с использованием фабричного метода Logger.For (), который использует тип T в качестве универсального параметра.Например:

class Foo
{
    private ILogger logger;
    public Foo(Ilogger logger) 
    {
        this.logger = logger;
    }
}

public void Main()
{
    var foo = new Foo(Logger.For<Foo>());
}

Есть ли способ, с помощью которого я могу зарегистрировать фабрику регистратора в Autofac таким образом, чтобы любой зарегистрированный тип Autofac T, имеющий параметр ILogger в своем конструкторе, автоматически получал экземпляр из Logger.For ()

1 Ответ

0 голосов
/ 15 декабря 2018

Есть пример чего-то подобного в документах Autofac , который использует log4net.Вместо Logger.For<T>() это LogManager.GetLogger(typeof(T)), но оно имеет место.

Первым шагом будет выяснить для данного объекта Type, как создать универсальный вызов метода.Вы можете сделать это с помощью MethodInfo.MakeGenericMethod.

Что-то вроде ...

private static object GetLogger(Type forType)
{
  // Get the LogManager.For<T>() method.
  var openMethod = typeof(LogManager).GetMethod("For", BindingFlags.Public | BindingFlags.Static);
  // Actually put the T in For<T>().
  var closedMethod = openMethod.MakeGenericMethod(forType);
  // Invoke the static method.
  return closedMethod.Invoke(null, null);
}

Теперь вы можете использовать пример практически из коробки.

public class LoggingModule : Autofac.Module
{
  private static void InjectLoggerProperties(object instance)
  {
    var instanceType = instance.GetType();

    // Get all the injectable properties to set.
    // If you wanted to ensure the properties were only UNSET properties,
    // here's where you'd do it. Also, update the typeof() call/filter thing
    // here as needed.
    var properties = instanceType
      .GetProperties(BindingFlags.Public | BindingFlags.Instance)
      .Where(p => p.PropertyType == typeof(ILog) && p.CanWrite && p.GetIndexParameters().Length == 0);

    // Set the properties located.
    foreach (var propToSet in properties)
    {
      propToSet.SetValue(instance, LogManager.GetLogger(instanceType), null);
    }
  }

  private static void OnComponentPreparing(object sender, PreparingEventArgs e)
  {
    e.Parameters = e.Parameters.Union(
      new[]
      {
        // Again, update the check here to ensure you're only filling in the
        // right parameters.
        new ResolvedParameter(
            (p, i) => p.ParameterType == typeof(ILog),
            // Here's the call to that generic method.
            (p, i) => GetLogger(p.Member.DeclaringType)
        ),
      });
  }

  protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
  {
    // Handle constructor parameters.
    registration.Preparing += OnComponentPreparing;

    // Handle properties.
    registration.Activated += (sender, e) => InjectLoggerProperties(e.Instance);
  }

  private static object GetLogger(Type forType)
  {
    // Get the LogManager.For<T>() method.
    var openMethod = typeof(LogManager).GetMethod("For", BindingFlags.Public | BindingFlags.Static);
    // Actually put the T in For<T>().
    var closedMethod = openMethod.MakeGenericMethod(forType);
    // Invoke the static method.
    return closedMethod.Invoke(null, null);
  }
}

Предостережение: Я на самом деле не скомпилировал это.Я довольно быстро схожу с макушки головы.Но это должно сделать это.

...