注册通用接口,无需访问实现

时间:2017-02-21 18:59:55

标签: c# generics dependency-injection autofac factory

我们已经在门面和工厂后面抽象了NLog的使用。我们的工厂返回一个NLogFacade实例,它或多或少地包装了一个NLog记录器。我们为工厂和记录器外观都有自己的接口,因此我们可以通过DI注册它们并在测试中模拟它们。工厂有一个通用的非泛型方法来创建ILogger,而ILogger接口存在于非泛型和通用的风格中。

ILogFactory

public interface ILogFactory
{
    ILogger GetLogger(string name);

    ILogger GetLogger<TCallingType>();
}

T

的ILogger
public interface ILogger<TOwner> : ILogger { }

ILogger

public interface ILogger
{
    // This is set as typeof(T).FullName if ILogger<T> is used
    // or if ILogFactory.GetLogger<T>() is used.
    string Name { get; }

    void Trace(string message, [CallerMemberName] string methodName = "");
    void Info(string message, [CallerMemberName] string methodName = "");
    void Error(string message, [CallerMemberName] string methodName = "");
    void Error(string message, Exception exception, [CallerMemberName] string methodName = "");
    void Error(Exception exception, [CallerMemberName] string methodName = "");
    void Fatal(string message, [CallerMemberName] string methodName = "");
    void Fatal(string message, Exception exception, [CallerMemberName] string methodName = "");
    void Fatal(Exception exception, [CallerMemberName] string methodName = "");
    void Log(LogLevel level, string message, [CallerMemberName] string methodName = "");
    void Log(LogLevel level, string message, Exception exception, [CallerMemberName] string methodName = "");
}

目前,我正在使用autofac注册我们的日志工厂,并将其注入我的构造函数。

builder.Register(context => LogService.GetInstance().LogFactory).As<ILogFactory>();

我无法访问ILogFactoryILogger的实施。它们都是公司程序集的内部类,其中包含日志记录。

我想做的是注册注册ILogger<T>

// Pseudo Registration
builder.Register(context => LogService.GetLogger<>).As<ILogger<>>();

// Construtor
public HomeController(ILogger<HomeController> logger) { }

有没有办法让我这样做,或者可以访问我的服务注入的类型?我可以使用非泛型调用来获取记录器,其中我传递记录器名称并注册非泛型ILogger。为了使用它,我需要知道正在注入服务的类型。像这样:

builder.Register(context => LogService.GetLogger(context.ResolvingType.FullName))
    .As<ILogger>();

当我查看Register上的ContainerBuilder方法时,我看不到任何可以让我确定要注入服务类型的内容。我还查看了OnActivatingOnActivated,它们没有我需要的任何类型信息。我有办法确定服务的类型吗?或者有更好的方法来解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

要注册打开的泛型,不能使用泛型类型的ref参数,因为那些需要在编译时解析。您必须将open generic作为Type参数传递给方法,而不是类型ref参数。

builder.Register(context => LogService.GetLogger(typeof(Logger<>))).As(typeof(ILogger<>));

当然,这意味着您需要将工厂设计更改为:

public interface ILogFactory
{
    ILogger GetLogger(string name);

    ILogger GetLogger<TCallingType>(); // Maybe you won't actually need this overload...

    ILogger GetLogger(Type type);
}

答案 1 :(得分:0)

我能够通过使用模块(as explained in their docs)解决这个问题,以便在将ILogger作为参数注入构造函数时解析它。 Preparing事件让我可以查看参数,并找出我的服务被注入的参数的拥有类型。

internal class LoggingDependencyRegistrationModule : Autofac.Module
{
    private static void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        e.Parameters = e.Parameters.Union(new[]
        {
            new ResolvedParameter(
                (p, i) => p.ParameterType == typeof(ILogger),
                (p, i) => LogService.GetLogger(p.Member.DeclaringType.FullName)),
        });
    }

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

然后在我的DI设置中,我注册了模块。

builder.RegisterModule(new LoggingDependencyRegistrationModule());

最后我可以将记录器注入我的构造函数

public HomeController(ILogger logger)
{
    // This correctly equals MyNamespace.Controllers.HomeController.
    string controllerName = logger.Name;
}