Как работает модуль регистрации для Autofac и NLog? - PullRequest
0 голосов
/ 02 марта 2019

Я все еще довольно новичок в Autofac и Nlog, и мне нужна помощь в понимании того, что происходит в моем модуле Autofac LoggingModule для Nlog.Он работает, как и ожидалось, благодаря injecting-nlog-with-autofacs-registergeneric .Но вместо того, чтобы просто копировать вставку, я хотел бы убедиться, что я понимаю, что происходит в каждом методе ( Загрузка & AttachToComponentRegistration ),Если бы вы могли пересмотреть мои мысли и уточнить, что я ошибаюсь (я уверен, что это немного), я был бы очень признателен.Заранее спасибо!

  • Цель базы данных с использованием Nlog
  • Внедрение зависимостей с использованием Autofac
  • ASP.NET MVC веб-приложение для обучения
  • Dvd Libaryприложение (DvdAdd, DvdEdit, DvdDelete, DvdList)

LoggingModule

public class LoggingModule : Module
{

    protected override void Load(ContainerBuilder builder)
    {
        builder
            .Register((c, p) => new LogService(p.TypedAs<Type>()))
            .AsImplementedInterfaces();
    }

    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing +=
            (sender, args) =>
            {
                var forType = args.Component.Activator.LimitType;

                var logParameter = new ResolvedParameter(
                    (p, c) => p.ParameterType == typeof(ILog),
                    (p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));

                args.Parameters = args.Parameters.Union(new[] { logParameter });
            };
    }

}

Мое понимание кода в Load()

c - параметр c , предоставленный выражению, является контекстом компонента (объектом IComponentContext), в котором создается компонент.Контекст, в котором можно получить доступ к службе или разрешить зависимости компонента.

p - IEnumerable с набором входящих параметров

AsImplementedInterfaces - Autofac позволяет своим пользователям регистрировать типы явно или неявно.В то время как « As » используется для явных регистраций, « AsImplementedInterfaces » и « AsSelf » используются для неявных.Другими словами, контейнер автоматически регистрирует реализацию для всех реализуемых им интерфейсов.

Мысли: Код метода Load регистрирует новый класс LogService (который представляет " c * 1060").* ") с типом регистратора (который представляет" p") в качестве параметра конструктора для класса LogService

Вопросы:

  • Правильны ли мои мысли выше?
  • Должно ли это быть SingleInstance или оно будет / будет жить так долго, как область действия вызывающих классов?(Я думаю о своей единице работы)

Мое понимание кода в AttachToComponentRegistration()

AttachToComponentRegistration Метод - Переопределить, чтобы прикрепить функциональность, специфичную для модуля, к регистрации компонента.

AttachToComponentRegistration Параметры:

  • IComponentRegistry componentRegistry - обеспечивает регистрацию компонентов в соответствии с предоставляемыми ими услугами.
  • IComponentRegistration регистрация - описывает логический компонент в контейнере.

регистрация. Подготовка - Запускается, когда требуется новый экземпляр.Экземпляр можно предоставить, чтобы пропустить обычный активатор, установив свойство Instance в предоставленных аргументах события.


var forType = args.Component.Activator.LimitType;

args = Autofac.Core.PreparingEventArgs - Запускается перед процессом активации, чтобы разрешить изменение параметров или предоставление альтернативного экземпляра.

Компонент = PreparingEventArgs.Component Свойство - Получает компонент, обеспечивающий активируемый экземпляр

Активатор = IComponentRegistration.Activator Property - Получает активатор, используемый для создания экземпляров.

LimitType = IInstanceActivator.LimitType Свойство - получает наиболее конкретный тип, которым являются экземпляры компонентаизвестно, что оно может быть преобразовано в.

Мысли в forType - Насколько я понимаю, эта переменная содержит Name и FullName вызывающего класса, с которого ведется логированиеВызывается ce?

Изображение отладчика forType

Вопросы:

  • Правильны ли мои мысли forType?

var logParameter = new ResolvedParameter(
                    (p, c) => p.ParameterType == typeof(ILog),
                    (p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));

ResolvedParameter - может использоваться как способ предоставления значений, динамически извлекаемых из контейнера, например, путем разрешения службы по имени.

Мысли о logParameter - Здесь я начинаю заблудиться.Так что он проверяет, что параметр имеет тип ILog, и если это так, то он разрешает его с помощью параметра конструктора и передает переменную forType?

Вопросы:

  • Верны ли мои мысли о logParameter выше?

args.Parameters = args.Parameters.Union(new[] { logParameter });

args.Parameters = PreparingEventArgs.Parameters Свойство - получает или задает параметры, передаваемые активатору.

args.Parameters.Union = Создает объединение наборов из двух последовательностей с использованием компаратора сравнения по умолчанию.Возвращает System.Collections.Generic.IEnumerable`1 , который содержит элементы из обеих входных последовательностей, исключая дубликаты.

Мысли о args.Parameters - я действительно не знаю, на данный момент, другиечем догадываться, что он возвращает коллекцию параметров и удаляет дубликаты?

Вопросы:

  • Не могли бы вы помочь мне рассказать, что происходит в * 1216?*?

Изображение отладчика logParameter Изображение таблицы базы данных Nlog


Класс LogService

public class LogService : ILog
{
    private readonly ILogger _log;

    public LogService(Type type)
    {
        _log = LogManager.GetLogger(type.FullName);
    }

    public void Debug(string message, params object[] args)
    {
        Log(LogLevel.Debug, message, args);
    }

    public void Info(string message, params object[] args)
    {
        Log(LogLevel.Info, message, args);
    }

    public void Warn(string message, params object[] args)
    {
        Log(LogLevel.Warn, message, args);
    }

    public void Error(string message, params object[] args)
    {
        Log(LogLevel.Error, message, args);
    }

    public void Error(Exception ex)
    {
        Log(LogLevel.Error, null, null, ex);
    }

    public void Error(Exception ex, string message, params object[] args)
    {
        Log(LogLevel.Error, message, args, ex);
    }

    public void Fatal(Exception ex, string message, params object[] args)
    {
        Log(LogLevel.Fatal, message, args, ex);
    }

    private void Log(LogLevel level, string message, object[] args)
    {
        _log.Log(typeof(LogService), new LogEventInfo(level, _log.Name, null, message, args));
    }

    private void Log(LogLevel level, string message, object[] args, Exception ex)
    {
        _log.Log(typeof(LogService), new LogEventInfo(level, _log.Name, null, message, args, ex));
    }

}

Интерфейс ILog

public interface ILog
{
    void Debug(string message, params object[] args);

    void Info(string message, params object[] args);

    void Warn(string message, params object[] args);


    void Error(string message, params object[] args);
    void Error(Exception ex);

    void Error(Exception ex, string message, params object[] args);

    void Fatal(Exception ex, string message, params object[] args);
}

1 Ответ

0 голосов
/ 03 марта 2019

Здесь много , чтобы распаковать здесь.Вы на самом деле не спрашиваете ответ на конкретный вопрос, а скорее описываете код и объясняете существующее решение, которое работает, поэтому я мог бы предложить опубликовать на StackExchange Code Review , если вам нужно гораздо больше, чем чтоЯ собираюсь дать вам здесь.Не пытаясь быть бесполезным, но, как если бы ваш вопрос: «Правильно ли я думаю?»и ответ «своего рода», есть много дискуссий по каждой отдельной точке, чтобы объяснить, почему «своего рода» является ответом (или «нет», или «да», в зависимости от обстоятельств).Он может превратиться в длинный ответ, за которым последуют дополнительные вопросы для уточнения, которые требуют еще дополнительных ответов ... и StackOverflow на самом деле не является дискуссионным форумом, способным на такие вещи.

[то есть яЯ, вероятно, потрачу на это час и напишу здесь ответ ... но я не могу обещать, что я действительно вернусь, чтобы что-то обсудить, потому что есть другие вопросы, на которые нужно ответить, и другие вещи, на которые мне нужно выделить время.StackOverflow действительно больше о "Как я ...?"или другие вещи, которые имеют один, достаточно конкретный ответ.]

Во-первых, я рекомендую погрузиться в себя с отладчиком на некоторых точках останова , чтобы на самом деле увидеть, что происходит.Например, вы спросили, что находится в LimitType в одной области - вы можете довольно легко ответить на этот вопрос, просто вставив точку останова на этой строке и посмотрев на значение.Это будет хороший способ получить дополнительные разъяснения самостоятельно - контрольные точки для победы.

Во-вторых, я рекомендую потратить некоторое время с документами Autofac . ЕстьЕсть много документации, которая может ответить на вопросы.

  • Модуль NLog здесь, кажется, основан на модуле log4net в документации , который имеет немного больше объяснения того, что происходитна.
  • * * * * * * * * * * * * * * * * * * * *

    * * * * * * * * * * * * * * *1030* * * * * * * * * * *1030* * * * * * * * * * * *1030*.может быть не совсем понятным, вместо того, чтобы пытаться обратиться к каждому пункту «мои мысли верны», позвольте мне сильно аннотировать модуль и надеюсь, что это прояснит ситуацию.

    // General module documentation is here:
    // https://autofac.readthedocs.io/en/latest/configuration/modules.html
    public class LoggingModule : Module
    {
      // Load basically registers types with the container just like
      // if you were doing it yourself on the ContainerBuilder. It's
      // just a nice way of packaging up a set of registrations so
      // they're not all in your program's "Main" method or whatever.
      protected override void Load(ContainerBuilder builder)
      {
        // This is a lambda registration. Docs here:
        // https://autofac.readthedocs.io/en/latest/register/registration.html#lambda-expression-components
        // This one uses both the component context (c) and the incoming
        // set of parameters (p). In this lambda, the parameters are NOT the set of constructor
        // parameters that Autofac has resolved - they're ONLY things that
        // were MANUALLY specified. In this case, it's assuming a TypedParameter
        // with a System.Type value is being provided manually. It's not going
        // to try resolving that value from the container. This is going hand-in-hand
        // with the logParameter you see in AttachToComponentRegistration.
        // Parameter docs are here:
        // https://autofac.readthedocs.io/en/latest/resolve/parameters.html
        // In general if you resolve something that has both manually specified parameters
        // and things that can be resolved by Autofac, the manually specified parameters
        // will take precedence. However, in this lambda it's very specifically looking
        // for a manually specified parameter.
        // You'll want to keep this as a default InstancePerDependency because you probably
        // want this to live as long as the thing using it and no longer. Likely
        // NLog already has object pooling and caching built in so this isn't as
        // expensive as you think, but I'm no NLog expert. log4net does handle
        // that for you.
        builder
          .Register((c, p) => new LogService(p.TypedAs<Type>()))
          .AsImplementedInterfaces();
      }
    
      // This method attaches a behavior (in this case, an event handler) to every
      // component registered in the container. Think of it as a way to run a sort
      // of "global foreach" over everything registered.
      protected override void AttachToComponentRegistration(
        IComponentRegistry componentRegistry,
        IComponentRegistration registration)
      {
        // The Preparing event is called any time a new instance is needed. There
        // are docs for the lifetime events but Preparing isn't on there. Here are the
        // docs and the issue I filed on your behalf to get Preparing documented.
        // https://autofac.readthedocs.io/en/latest/lifetime/events.html
        // https://github.com/autofac/Documentation/issues/69
        // You can see the Preparing event here:
        // https://github.com/autofac/Autofac/blob/6dde84e5b0a3f82136a0567a84da498b04e1fa2d/src/Autofac/Core/IComponentRegistration.cs#L83
        // and the event args here:
        // https://github.com/autofac/Autofac/blob/6dde84e5b0/src/Autofac/Core/PreparingEventArgs.cs
        registration.Preparing +=
          (sender, args) =>
            {
              // The Component is the thing being resolved - the thing that
              // needs a LogService injected. The Component.Activator is the
              // thing that is actually going to execute to "new up" an instance
              // of the Component. The Component.Activator.LimitType is the actual
              // System.Type of the thing being resolved.
              var forType = args.Component.Activator.LimitType;
    
              // The docs above explain ResolvedParameter - basically a manually
              // passed in parameter that can execute some logic to determine if
              // it satisfies a constructor or property dependency. The point of
              // this particular parameter is to provide an ILog to anything being
              // resolved that happens to have an ILog constructor parameter.
              var logParameter = new ResolvedParameter(
    
                // p is the System.Reflection.ParameterInfo that describes the
                // constructor parameter that needs injecting. c is the IComponentContext
                // in which the resolution is being done (not used here). If this
                // method evaluates to true then this parameter will be used; if not,
                // it will refuse to provide a value. In this case, if the parameter
                // being injected is an ILog, this ResolvedParameter will tell Autofac
                // it can provide a value.
                (p, c) => p.ParameterType == typeof(ILog),
    
                // p and c are the same here, but this time they're used to actually
                // generate the value of the parameter - the ILog instance that should
                // be injected. Again, this will only run if the above predicate evaluates
                // to true. This creates an ILog by manually resolving from the same
                // component context (the same lifetime scope) as the thing that
                // needs the ILog. Remember earlier that call to p.AsTyped<Type>()
                // to get a parameter? The TypedParameter thing here is how that
                // value gets poked in up there. This Resolve call will effectively
                // end up calling the lambda registration.
                (p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));
    
              // The thing being resolved (the component that consumes ILog) now
              // needs to be told to make use of the log parameter, so add it into
              // the list of parameters that can be used when resolving that thing.
              // If there's an ILog, Autofac will use this specified parameter to
              // fulfill the requirement.
              args.Parameters = args.Parameters.Union(new[] { logParameter });
            };
        }
    }
    

    Что-то отсутствует вв примере модуля log4net - это возможность внедрения свойства для регистратора.Однако я не собираюсь решать это здесь;Вы можете посмотреть пример прямо в документации и использовать его как упражнение для работы, если вам нужна эта функциональность.

    Надеюсь, это поможет.Я, вероятно, не вернусь, чтобы ответить на дополнительные вопросы, поэтому, если этого недостаточно, я очень, очень рекомендую установить некоторые контрольные точки, может быть, установить несколько крошечных тестов с минимальным повторением, и тому подобное, исделать более глубокое исследование, чтобы получить ясность.Честно говоря, одно дело, чтобы кто-то еще объяснил это, но другое - на самом деле увидеть это в действии и погрузиться в источник различных проектов.С последним подходом вы получите более полное понимание, даже если оно потенциально не такое быстрое.

...