使用Ninject(或其他一些容器)如何找出请求服务的类型?

假设我有一个服务接口:

public interface IFooService { void DoSomething(); } 

并且该服务的具体实现是通用的:

 public class FooService : IFooService { public virtual void DoSomething() { } } 

我还有一些需要IFooService实例的其他类:

 public class Bar { private IFooService _fooService; public Bar(IFooService fooService) { this._fooService = fooService; } } 

我需要连接我的IoC容器,这样当创建Bar时,它会传递一个FooService 的构造函数参数。 还有很多其他课程就像Bar一样。 每个人都可能需要传递给他们的FooService 实例,其中TRequestingClass是需要IFooService实例的类的类型。 我不需要向IFooService的消费者公开这个怪癖。 他们应该关心的是他们可以调用他们传递的IFooService的方法。 他们不应该知道他们传递的IFooService的具体实现需要任何特殊的构造。

FooService 的一个可接受的替代方案是一个非generics类,它的构造函数中包含一个字符串参数,该类包含要为其创建的类的名称。 即:

 public class FooService : IFooService { public FooService(string requestingClassName) { } } 

如何通过这种方式连接我的IoC容器来构建依赖项?

如果你为什么我想要这样一个奇怪的结构而感到困惑,那么考虑当你得到一个用log4net.LogManager.GetLogger(typeof(SomeClass))创建的ILog时log4net如何工作得最好。 我不想通过引用log4net来丢弃我的代码,所以我想编写一个简单的ILogger接口,并使用以下内容实现它:

 public class GenericLogger : 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接口:

 public class ILogger : ILogger { } public class GenericLogger : ILogger { ... } 

然后依靠generics类型推断来获得正确的类型。 例如,在Ninject中,您需要以下绑定:

 Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>)); 

那么您的消费类型将如下所示:

 public class FooService : IFooService { public FooService(ILogger logger) { ... } } 

如果您坚决反对ILogger界面,您可以做一些更有创意的事情,比如自定义提供程序,它会读取IContext以确定父类型。

 public class GenericLogger : ILogger { public class GenericLogger(Type type) { ... } } public class LoggerProvider : Provider { 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(ParameterList); 

我还没有完全读过Ninject参数列表,但它似乎是一种使用IParameterList注入参数类型的方法。

编辑:

看起来这会像这样工作:

 ILog = kernel.Get(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( new ConstructorArgument[] { new ConstructorArgument( "ClassName", this.GetType().Name ) } ); } } public class Tester { public Tester(string ClassName) { Console.WriteLine("Called From {0}", ClassName); } } 

编辑:

另一种可能有用的方法是使用The Bind()。To()。WithConstructorArgument()绑定,当与自绑定或.WhenInjectedInto()条件一起使用时,它可能很有用。

要详细说明自定义提供者的想法,这里有一个简单的方法…

 internal class LogModule : StandardModule { private class log4netILogProvider : SimpleProvider { protected override ILog CreateInstance(IContext context) { return LogManager.GetLogger(context.Instance.GetType()); } } public override void Load() { Bind().ToProvider(new log4netILogProvider()); } } 

然后在注入的类中:

 [Inject] public ILog logger { get; set; }