工厂用于Autofac的通用接口

时间:2017-05-03 02:40:12

标签: c# generics autofac

我在Autofac容器中注册了ILoggerFactory实现。我希望能够使用Autofac注册以下类并能够实例化它:

public class Test
{
    public Test(ILogger<Test> logger)
    {
        logger.LogInformation("Works!");
    }
}

为此,我需要能够“教导”Autofac为任何类ILogger<T>生成实现T的实例。有一个ILoggerFactory扩展方法CreateLogger<T>()CreateLogger(Type)能够生成所需类型的实例,但我无法理解如何让Autofac调用它们。

如果我想要非通用ILogger,我会写

containerBuilder.Register(c => c.Resolve<ILoggerFactory>().CreateLogger("default"))

但是我不知道如何为通用的做什么。有什么指针吗?

更新:在阅读了更多documentation后,我想出了这个:

public class LoggingModule: Autofac.Module
{
    private readonly MethodInfo mi;

    public LoggingModule()
    {
        this.mi = typeof(LoggerFactoryExtensions)
            .GetMethod(nameof(LoggerFactoryExtensions.CreateLogger), new[] { typeof(ILoggerFactory) });
    }

    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += OnComponentPreparing;
    }

    private void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        e.Parameters = e.Parameters.Union(
            new[]
            {
                new ResolvedParameter(
                    (p, i) => IsGenericLoggerParameter(p.ParameterType),
                    (p, i) => CreateLogger(this.mi, p.ParameterType, i.Resolve<ILoggerFactory>())
                    )
            });
    }

    private static bool IsGenericLoggerParameter(Type parameterType)
    {
        return parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(ILogger<>);
    }

    private static object CreateLogger(MethodInfo mi, Type typeArg, ILoggerFactory loggerFactory)
    {
        Type genericArg = typeArg.GetGenericArguments().First();
        MethodInfo generic = mi.MakeGenericMethod(new[] { genericArg });
        return generic.Invoke(null, new[] { loggerFactory });
    }
}

然后:

        containerBuilder.RegisterType<Test>().AsSelf();
        containerBuilder.RegisterModule<LoggingModule>();
        var container = containerBuilder.Build();
        var test = container.Resolve<Test>();

的工作原理。问题是我不完全理解它是如何工作的(更重要的是它可能会如何破坏),所以我仍然在寻找更优雅和/或可理解的解决方案。

4 个答案:

答案 0 :(得分:2)

我找到了一个简单的答案,但仅针对ILogger<T>包中的Microsoft.Extensions.Logging:使用ServiceCollection包中的Microsoft.Extensions.DependencyInjection

        IServiceCollection services = new ServiceCollection();
        services.AddLogging(); // all the magic happens here
        // set up other services 
        var builder = new ContainerBuilder();
        builder.Populate(services); // from the Autofac.Extensions.DependencyInjection package
        IContainer container = builder.Build();
        Test t = container.Resolve<Test>();

答案 1 :(得分:1)

我在一年后回答为 AutoFac v6 提供干净的解决方案

如果你想添加 Microsoft.Extensions.Logging ...

将 ILogger(来自 Microsoft.Extensions.Logging)添加到 AutoFac 的这种最简洁的方法是注册 Microsoft.Extensions.Logging.ILoggerFactory 实现和 Microsoft.Extensions.Logging.Logger 泛型类。

Logger 类是装饰器类,在 Microsoft.Extensions.Logging.Abstractions 库中,它包装了实际的 ILogger 实现,但实际上并没有实现具体的记录器(控制台记录器、EventLog 记录器等)。 Logger 具有参数类型为 ILoggerFactory 的构造函数,用于创建 ILogger 的具体实现。

解决方案

static ContainerBuilder CreateBuilder()
{
    var builder = new ContainerBuilder();

    builder.RegisterInstance(LoggerFactory.Create(config => config
        .AddConsole()
        .SetMinimumLevel(LogLevel.Trace)
    ));

    builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>));

    return builder;
}

如果要注册具有复杂构造函数逻辑的通用服务...

至于问题的字面答案:我建议使用 RegisterGeneric 方法。

示例

builder.RegisterGeneric((context, types) =>
{
    var factory = context.Resolve<ILoggerFactory>();
    return factory.CreateLogger(types[0].FullName);
}).As(typeof(ILogger<>));

答案 2 :(得分:0)

是的,很高兴您发布了此信息: 在您的示例中,我将其用于ILogger和ILogger

  public class LoggingDependencyRegistrationModule : Autofac.Module
{
    private readonly ILoggerFactory _lf;
    private readonly MethodInfo _mi;
    private readonly Type _t;

    public LoggingDependencyRegistrationModule(ILoggerFactory lf)
    {
        _lf = lf;
        _mi = typeof(LoggerFactoryExtensions).GetMethod(nameof(LoggerFactoryExtensions.CreateLogger), new[] { typeof(ILoggerFactory) });
        _t = typeof(ILogger);
    }
    private void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        var t = e.Component.Activator.LimitType;
        e.Parameters = e.Parameters.Union(new[]
        {
            new ResolvedParameter(
                (p, i) =>
                            _t.IsAssignableFrom(p.ParameterType),
               (p,i)=>{

                    if(p.ParameterType == _t)
                       return _lf.CreateLogger(t.ToString());

                    return  CreateLogger(_mi, p.ParameterType, _lf);
                   }
               )
        });
    }
    private static object CreateLogger(MethodInfo mi, Type typeArg, ILoggerFactory loggerFactory)
    {
        Type genericArg = typeArg.GetGenericArguments().First();
        MethodInfo generic = mi.MakeGenericMethod(new[] { genericArg });
        return generic.Invoke(null, new[] { loggerFactory });
    }


    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += OnComponentPreparing;
    }
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterInstance(_lf).SingleInstance();
        base.Load(builder);
    }
}

答案 3 :(得分:0)

使用Autofac并需要将Microsoft ILogger 注入对象时,必须将以下内容添加到ContainerBuilder

ContainerBuilder builder = new ContainerBuilder();
var loggerFactory = new LoggerFactory();
//loggerFactory.AddProvider(LogProvider) //If you need another provider than Microsoft logger
builder.RegisterInstance(loggerFactory).As<ILoggerFactory>().SingleInstance();
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance();

需要注入ILogger 的对象现在将获得正确的ILogger