使用Ninject(或其他容器),我怎样才能找出被请求服务的类型?
-
23-08-2019 - |
题
假设我有一个服务的接口:
public interface IFooService
{
void DoSomething();
}
和具体实现该服务,它是一个通用的:
public class FooService<TRequestingClass> : IFooService
{
public virtual void DoSomething() { }
}
和予有需要IFooService的实例一些其他类:
public class Bar
{
private IFooService _fooService;
public Bar(IFooService fooService)
{
this._fooService = fooService;
}
}
我需要线了我的IoC容器,从而获取创建栏时,它就会通过FooService接口<栏>的构造函数的参数。还有许多其他类一样吧。其中TRequestingClass是需要IFooService的实例类的类型每个可能还需要FooService接口
这是可接受的alternatative到FooService接口
public class FooService : IFooService
{
public FooService(string requestingClassName) { }
}
我怎样才能线了我的IoC容器构建依赖这种方式?
如果你是困惑,为什么我会想这样一个奇怪的结构,可以考虑当你得到的是它和log4net.LogManager.GetLogger(typeof运算(SomeClass的))创建的ILog的log4net的是如何工作的最好的。我不想我的垃圾带的引用代码log4net的,所以我想编写一个简单的ILogger接口,并与像这样实现:
public class GenericLogger<T> : ILogger
{
private readonly ILog log;
public GenericLogger()
{
this.log = log4net.LogManager.GetLogger(typeof(T));
}
public void Debug(object message)
{
this.log.Debug(message);
}
/* .... etc .... */
}
解决方案
在最简单的方式是创建一个ILogger<T>
接口:
public class ILogger<T> : ILogger { }
public class GenericLogger<T> : ILogger<T> { ... }
然后依托通用的类型推断,以获得正确的类型。例如,在Ninject,下面的结合是所有你需要:
Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>));
那么你的消费类型应该是这样的:
public class FooService : IFooService {
public FooService(ILogger<FooService> logger) { ... }
}
如果你是坚决反对的ILogger<T>
界面,你可以做一些更有创造性,就像一个自定义提供,其内容的IContext
确定父类型。
public class GenericLogger : ILogger {
public class GenericLogger(Type type) { ... }
}
public class LoggerProvider : Provider<ILogger> {
public override ILogger CreateInstance(IContext context) {
return new GenericLogger(context.Target.Member.ReflectedType);
}
}
然后消耗类型将工作是这样的:
public class FooService : IFooService {
public FooService(ILogger logger) { ... }
}
其他提示
如果我不是误解你,为什么不干脆让您的GericLogger构造带一个参数是对象的类型。然后这样做:
ILog = kernel.Get<ILog>(ParameterList);
我还没有Ninject参数列表读到完全还,但它似乎是注入使用IParameterList参数类型的一种方法。
编辑:
看起来这将工作是这样的:
ILog = kernel.Get<ILog>(new ConstructorArgument[] {
new ConstructorArgument("ClassName", this.GetType().Name)
})
然后你有你的ILog
class GenericLogger : Ilog
{
GenericLogger(string ClassName) {};
}
我没有测试这一点,只是似乎是从Ninject源(我看最近Ninject2树)
编辑:
您想通过ConstructorArgument,没有参数。更新,以反映。
此代码打印: “被叫由init”
class Init {
public void Run() {
StandardKernel kernel = new StandardKernel( new MyLoader() );
kernel.Get<Tester>( new ConstructorArgument[] {
new ConstructorArgument( "ClassName",
this.GetType().Name
)
} );
}
}
public class Tester {
public Tester(string ClassName) {
Console.WriteLine("Called From {0}", ClassName);
}
}
编辑:
,这可能是有用的另一种方法是使用bind()。向()。WithConstructorArgument()结合,与任一的自粘结或用.WhenInjectedInto()条件下使用时可能是有用的。
要阐述的定义提供想法,这里有一个简单的方法来做到这一点...
internal class LogModule : StandardModule
{
private class log4netILogProvider : SimpleProvider<log4net.ILog>
{
protected override ILog CreateInstance(IContext context)
{
return LogManager.GetLogger(context.Instance.GetType());
}
}
public override void Load()
{
Bind<log4net.ILog>().ToProvider(new log4netILogProvider());
}
}
然后,在类被注射:
[Inject]
public ILog logger { get; set; }