使用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; }