【问题标题】:How can I resolve the correct logger type using AutoFac?如何使用 AutoFac 解析正确的记录器类型?
【发布时间】:2014-01-17 18:47:35
【问题描述】:

我正在更新一个使用 AutoFac 的旧项目,并且我想将 NLog 与 Simple Logging Facade (SLF) 一起使用

我过去曾在 Ninject 中使用过它,而且它非常容易设置,我只需要执行以下操作:

kernel.Bind<ILogger>().ToMethod(x => LoggerFactory.GetLogger(x.Request.Target.Member.ReflectedType));

输出会是这样的:

NLogNinjectSlf.Services.MyService 2013-12-30 15:21:10.5782 DEBUG 来自注入 Logger 的日志

小菜一碟

但现在我必须使用 AutoFac,我不知道如何获取需要记录器的 目标类型

例如,如果我有以下接口/类:

public interface IMyService
{
    void DoSomething();
}

public class MyService : IMyService
{
    private readonly ILogger _logger;

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

    public void DoSomething()
    {
        _logger.Debug("Log from injected Logger");
    }
}

我希望能够获取 MyService 类的类型以将其用作我的记录器的名称

在 AutoFac 中这是我迄今为止尝试过的:

var containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<MyService>().As<IMyService>();

containerBuilder.Register(x =>
{
    // TODO: Get the correct type
    return LoggerFactory.GetLogger(x.GetType());
}).As<ILogger>();

顺便说一句:我在 SLF4Net 后面使用 NLog,但并不是真正需要解决主要问题...

【问题讨论】:

标签: c# logging autofac nlog slf4net


【解决方案1】:

感谢nemesv对我帮助很大

这是我最终使用的代码

顺便说一句。如果您愿意,您可以删除注入属性的代码,然后在所有类中使用 DI 注入 ILogger,这将提高性能

public class LoggingModule : Module
{
    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
        IComponentRegistration registration)
    {
        registration.Preparing += OnComponentPreparing;

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

    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.
        var properties = instanceType
          .GetProperties(BindingFlags.Public | BindingFlags.Instance)
          .Where(p => p.PropertyType == typeof(ILogger) && p.CanWrite && p.GetIndexParameters().Length == 0);

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

    private 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 (ILogger),
                    (p, i) => LoggerFactory.GetLogger(t))
            });
    }
}

然后注册模块:

containerBuilder.RegisterModule<LoggingModule>();

【讨论】:

  • 非常感谢这段代码。我在 Azure Web 角色中使用它,出于某种原因,它在本地 azure 环境中完美运行,但构造函数注入在实际 Azure Web 角色中不起作用(但属性注入)。有什么想法吗?
  • 更正上述内容。我在整个解决方案中都使用它,出于某种原因,这在正常情况下非常有效,但是构造函数注入在 webapi actionfilter 类中不起作用(但属性注入起作用)。有什么想法吗?
【解决方案2】:

似乎使用 Autofac 4.3.0 你可以简单地使用 OnComponentPreparing() 回调而不使用特殊的属性注入技术。 ctor() 和属性的值都将被注入:

public class LoggingModule : Module
{
 protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
        IComponentRegistration registration)
    {
        registration.Preparing += Registration_Preparing;
    }

private static void Registration_Preparing(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) => LogFactory.CreateWithType(t))
            });
    }
}

我还发现了一些不错的技巧,仅当您需要控制记录器的生命周期时才可用于属性注入。

public class LoggingModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        const string propertyNameKey = "Autofac.AutowiringPropertyInjector.InstanceType";

        builder.RegisterType<NLogLogger>()
            .As<ILog>()
            .OnPreparing(x =>
            {
                var firstParam = x.Parameters?
                    .OfType<NamedParameter>()
                    .FirstOrDefault(p => p.Name == propertyNameKey);

                if (null == firstParam)
                {
                    return;
                }

                var valueType = firstParam.Value;
                x.Parameters = x.Parameters.Union(
                    new[]
                    {
                        new ResolvedParameter(
                            (p, i) => p.Name == "type",
                            (p, i) => valueType)
                    });
            })
            .InstancePerDependency();

NLogLogger 本身在哪里

public class NLogLogger : ILog
{
    private readonly Logger _log;

    public NLogLogger()
    {
        _log = LogManager.GetCurrentClassLogger();
    }

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-25
    • 2014-11-06
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多