Autofac在运行时如何解析具有构造函数参数的泛型类型

4

我有一个通用类 Logger<TProvider>,其中 TProvider 必须实现接口 ILogProvider。我想使用 Autofac 配置依赖注入。

这个类还有一个构造函数:

public Logger(LogType loggerType)

目前使用方式如下所示:

var logger = new Logger<Log4NetLogProvider>(LogType.CMS);

我想知道这个是否可以进行依赖注入,或者由于需要类型/构造函数参数而不可能实现?
我知道有一个RegisterGeneric方法,例如:
builder.RegisterGeneric(typeof(Logger<>)).AsSelf()

我想知道如何告诉Autofac传入哪个TProvider,并提供构造函数参数LogType?或者这不适合依赖注入吗?


1
你是否认为使用LogType.CMS构建的记录器可以与使用LogType.Foo构建的记录器互换使用?如果不是,那么你的设计应该通过单独的接口ICmsLogger和IFooLogger来传达这一点。然后你的服务将依赖于特定的接口。 - Mike Hixson
@MikeHixson - 那真是个非常棒的想法,我之前没有想到过。 - DGibbs
@MikeHixson 这个方法可以工作,但是我必须针对记录器类指定所有接口类型,例如:public class Logger : IFileLogger, IKenticoLogger, IKenticoAndFileLogger, ISmtpLogger,这有点丑陋。是否有一种方法可以给它一个“基本”接口,例如 ILogger,每个接口都基于它? - DGibbs
似乎你的软件一直拥有着一个接口的长列表,只是以Logger的不可互换实例的形式隐藏在设计中。现在它们已经浮出水面并被认为“丑陋”,也许是时候采取一些行动了。也许重新考虑拥有这么多不同种类的记录器?就我个人而言,我认为拥有一个接口的长列表没有任何问题。 - Mike Hixson
@MikeHixson - 或许“丑陋”这个词用得不太恰当。只是看起来好像应该可以简单地指定“ILogger”作为其他接口都派生自它,但我想可能不行。 - DGibbs
我相信你所要求的是可能实现的,但使用类似于通用 ILogger 这样的东西基本上会让你回到起点 - 服务需要指定它们需要哪种依赖项。也许你只想这样做:https://autofaccn.readthedocs.io/en/latest/advanced/keyed-services.html#resolving-with-an-index - Mike Hixson
2个回答

3
当注册类型时,您可以尝试使用WithParameter扩展。
//using named parameter
builder.RegisterGeneric(typeof(Logger<>))
    .AsSelf()
    .WithParameter("loggerType", LogType.CMS);

//OR - using typed parameter
builder.RegisterGeneric(typeof(Logger<>))
   .AsSelf()
   .WithParameter(new TypedParameter(typeof(LogType), LogType.CMS));

参考 向 Register 传递参数


我不确定这是否可行,因为 LogType 可以是任意数量的枚举值,例如它不总是 .CMS - DGibbs
能否使用泛型类型来实现这个:https://docs.autofac.org/en/latest/register/parameters.html#parameters-with-lambda-expression-components 例如 BaseRepository??? - IbrarMumtaz

0
我处理你的问题的方式如下:
我像你一样注册了泛型类。
builder.RegisterGeneric(typeof(Logger<>));

然后我使用类似以下的类型进行注册:

 builder.RegisterType<Logger<TProvider>>()
                .As<ILogger<TProvider>>()
                .WithParameter("loggerType", LogType.CMS);

或者你可以使用类型化参数来实现:

builder.RegisterType<Logger<TProvider>>()
                    .As<ILogger<TProvider>>()
                    .WithParameter(TypedParameter.From(LogType.CMS)));

使用参数替换TProvider。

builder.RegisterType<Logger<LogProvider>>()
                        .As<ILogger<LogProvider>>()
                        .WithParameter(TypedParamater.From(LogType.CMS)));

我脑海中首先想到的是为每个你想要注入的记录器调用builder.RegisterType<Logger<LogProvider>>().As<ILogger<LogProvider>>(); builder.RegisterType<Logger<LogProvider1>>().As<ILogger<LogProvider1>>();。你也可以为每个实例传递相应的LogType - Givko
好的。那么在页面上(Web表单)会是什么样子呢?例如,对于服务,我有一个公共属性public IMockService MockService { get; set; },它被注入了,那么对于记录器,由于我需要提供构造函数参数,我该怎么做呢? - DGibbs
这是一个不同的场景。如果您想注入一个属性,您可以使用属性注入。请参考链接访问Autofac文档有关属性注入的内容。在注册需要由名称注入属性的类型时,您可以使用builder.RegisterType<A>().WithProperty("PropertyName", propertyValue); - Givko
抱歉,我的表述不够清楚。记录器作为页面(web forms)的属性被注入,因此它并不是传统意义上的构造函数参数 (injected as constructor parameter)。所以在我的.aspx页面中,我会有一个公共的Logger<Log4NetProvider> Log { get; set; }。我该如何把LogType传递进去呢? - DGibbs
谢谢 - 我看到我可以通过属性注入传递一个值,但是这是在注册时进行的,而为了使其可行,需要针对每种情况进行,例如某些页面可能需要使用 LogType.CMSLog,而其他页面可能需要 LogType.Console 等。 - DGibbs
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接