Почему Autofac не может найти log4net с помощью LogInjectionModule?

Как обсуждалось на Autofac Wiki, лучший способ автоматически внедрить log4net.ILog реализацию для класс должен использовать LogInjectionModule. Реализация этого модуля приведена в вики-статье:

public class LogInjectionModule : Module
{
    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        if (registration == null) throw new ArgumentNullException("registration");
        registration.Preparing += OnComponentPreparing;
    }

    static void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        var t = e.Component.Activator.LimitType;
        e.Parameters = e.Parameters.Union(new[]
                                        {
                                            new ResolvedParameter((p, i) => p.ParameterType == typeof(ILog), (p, i) => LogManager.GetLogger(t))
                                        });
    }
}

Я включаю этот модуль вместе с моим собственным модулем для настройки некоторых компонентов:

var builder = new ContainerBuilder();
builder.RegisterModule(new Common.Logging.LogInjectionModule());
builder.RegisterModule(new DataLayer.DataLayerModule("connectionstring"));
builder.RegisterType<SomeServiceType>();
AutofacHostFactory.Container = builder.Build();

Внутри DataLayerModule я создаю тип следующим образом:

protected override void Load(ContainerBuilder builder)
{
    builder.Register(c => new DataAccess(this.ConnectionString, c.Resolve<ILog>()))
        .As<IDataAccess>();

    // Some other type registrations...
}

Когда мое приложение пытается создать объект IDataAccess, я получаю следующее исключение:

Запрошенная служба «log4net.ILog» не зарегистрирована. Чтобы избежать этого исключения, либо зарегистрируйте компонент для предоставления службы, проверьте регистрацию службы с помощью IsRegistered(), либо используйте метод ResolveOptional() для разрешения дополнительной зависимости.

Я знаю, что на вики-странице упоминается, что метод внедрения работает только для инъекция конструктора. Однако я думал, что это то, что я делаю здесь. Я ошибся?


person Adrian Clark    schedule 03.11.2011    source источник


Ответы (1)


Изменить

При дальнейшем расследовании мой первоначальный ответ не решает вашу проблему (я оставил его ниже для справки). Проблема в том, что ваш код обходит LogInjectionModule, создавая new экземпляр DataAccess и напрямую разрешая ILog. Это не шаблон использования, поддерживаемый LogInjectionModule, который предназначен для предоставления экземпляра ILog, соответствующего типу экземпляра, активируемого Autofac (метод LogManager.GetLogger() log4net должен знать о типе, который использует регистратор).

Чтобы обойти это, у вас есть два варианта. Самый простой — предоставить экземпляр регистратора напрямую, без использования LogInjectionModule:

builder.Register(c => 
            new DataAccess(
                this.ConnectionString, 
                LogManager.GetLogger(typeof(DataAccess)))
       .As<IDataAccess>();

В качестве альтернативы (и более аккуратно) вы можете зарегистрировать DataAccess обычным способом Autofac, а затем зарегистрировать IDataAccess с помощью именованные параметры для указания строки подключения, которую Autofac передает конструктору. С помощью этого метода вы позволяете Autofac использовать LogInjectionModule при разрешении параметра ILog конструктора.

builder.RegisterType<DataAccess>();
builder.Register(c => 
            c.Resolve<DataAccess>(
               new NamedParameter("connectionString", this.ConnectionString)))
       .As<IDataAccess>();

(Обратите внимание, что в этом коде предполагается, что параметр строки подключения в конструкторе DataAccess имеет имя «connectionString»).

Преимущество второго метода заключается в том, что ваш модуль не ссылается напрямую на класс LogManager log4net, поэтому единственная связь - с интерфейсом ILog.


Исходный (неправильный) ответ

Этот пример кода предназначен для Autofac v2.0 (первоначально я предоставил образец на этой вики-странице). Код моего текущего рабочего модуля (для Autofac v2.4.5):

public class LogInjectionModule : ComponentInjectionModule
{
    /// <summary>
    ///     Called when Autofac is preparing a component for activation.
    /// </summary>
    /// <param name = "sender">
    ///     The sender.
    /// </param>
    /// <param name = "e">
    ///     The <see cref = "ActivatingEventArgs{T}" /> instance containing the event data.
    /// </param>
    protected override void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        Enforce.ArgumentNotNull(e, "e");

        Type t = e.Component.Activator.LimitType;
        e.Parameters = e.Parameters.Union(
            new[]
                {
                    new ResolvedParameter(
                        (p, i) => p.ParameterType == typeof(ILog), (p, i) => LogManager.GetLogger(t))
                });
    }
}

Этот код основан на моем классе ComponentInjectionModule, который определен ниже. Есть некоторые тонкие различия в том, как этот модуль подключается к процессу регистрации/активации.

/// <summary>
///     Base module for injecting into registrations when they are prepared/activated.
/// </summary>
public class ComponentInjectionModule : IModule
{
    /// <summary>
    ///     Apply the module to the component registry.
    /// </summary>
    /// <param name = "componentRegistry">
    ///     Component registry to apply configuration to.
    /// </param>
    public void Configure(IComponentRegistry componentRegistry)
    {
        Enforce.ArgumentNotNull(componentRegistry, "componentRegistry");

        foreach (var registration in componentRegistry.Registrations)
        {
            this.AttachToComponentRegistration(registration);
        }

        componentRegistry.Registered +=
            (sender, e) => this.AttachToComponentRegistration(e.ComponentRegistration);
    }

    /// <summary>
    ///     Attaches to the <see cref = "IComponentRegistration.Preparing" /> event of a component registration.
    /// </summary>
    /// <param name = "registration">
    ///     The registration whose Preparing event will be attached.
    /// </param>
    protected virtual void AttachToComponentRegistration(IComponentRegistration registration)
    {
        Enforce.ArgumentNotNull(registration, "registration");

        registration.Preparing += this.OnComponentPreparing;
        registration.Activating += this.OnComponentActivating;
        registration.Activated += this.OnComponentActivated;
    }

    /// <summary>
    /// Called when Autofac has activated a component.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="ActivatedEventArgs{T}"/> instance containing the event data.</param>
    [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", Justification = "Not an event handler")]
    protected virtual void OnComponentActivated(object sender, ActivatedEventArgs<object> e)
    {
    }

    /// <summary>
    /// Called when Autofac is activating a component.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="ActivatingEventArgs{T}"/> instance containing the event data.</param>
    [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", Justification = "Not an event handler")]
    protected virtual void OnComponentActivating(object sender, ActivatingEventArgs<object> e)
    {
    }

    /// <summary>
    /// Called when Autofac is preparing a component for activation.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="ActivatingEventArgs{T}"/> instance containing the event data.</param>
    [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", Justification = "Not an event handler")]
    protected virtual void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
    }
}
person Rich Tebb    schedule 03.11.2011
comment
Спасибо за исчерпывающий ответ! Я подумал, что неправильно понял, как работает LogInjectionModule. Пошел с более чистым вариантом, который вы дали, и он работает. - person Adrian Clark; 04.11.2011