为什么Autofac无法使用"LogInjectionModule"找到log4net?

Adr*_*ark 1 log4net inversion-of-control autofac

正如在Autofac Wiki上所讨论的,为类自动注入log4net.ILog实现的最佳方法是使用LogInjectionModule.该模块的实现在wiki文章中给出:

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))
                                        });
    }
}
Run Code Online (Sandbox Code Playgroud)

我将此模块与我自己的模块一起包括在内以配置一些组件:

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

里面DataLayerModule我构造类型,如下所示:

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

    // Some other type registrations...
}
Run Code Online (Sandbox Code Playgroud)

当我的应用程序尝试构造一个IDataAccess对象时,我得到以下异常:

请求的服务'log4net.ILog'尚未注册.要避免此异常,请注册组件以提供服务,使用IsRegistered()检查服务注册,或使用ResolveOptional()方法解析可选依赖项.

我知道在维基页面上提到注入方法仅适用于构造函数注入.但是我认为这就是我在这里所做的.我错了吗?

Ric*_*ebb 5

编辑

在进一步调查中,我的原始答案并没有解决你的问题(我把它留在下面作为参考).问题是您的代码LogInjectionModule通过直接创建和解析new实例来绕过.这不是一个支持的使用模式,它旨在提供一个与Autofac激活的实例类型相匹配的实例(log4net的方法需要知道使用该记录器的类型).DataAccessILogLogInjectionModuleILogLogManager.GetLogger()

要解决此问题,您有两种选择.最简单的是直接提供记录器实例而不使用LogInjectionModule:

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

或者(更干净地),您可以DataAccess使用正常的Autofac方式注册,然后IDataAccess使用命名参数进行注册,以指定Autofac传递给构造函数的连接字符串.使用此技术,您可以使Autofac LogInjectionModule在解析构造函数的ILog参数时使用.

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

(请注意,此代码假定DataAccess构造函数中的连接字符串参数名为"connectionString").

第二种技术的优点是您的模块不LogManager直接引用log4net的类,因此唯一的耦合是ILog接口.


原始(不正确)答案

该代码示例适用于Autofac v2.0(我最初在该Wiki页面上提供了示例).我当前工作模块的代码(适用于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))
                });
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码依赖于我在下面定义的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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)