Cannot resolve dependency from child kernel
Closed this issue · 7 comments
[Test]
public void Test()
{
var kernel = new StandardKernel();
kernel.Bind<Func<string>>().ToConstant(() => "foo");
kernel.Get<Func<string>>()().Should().Be("foo");
var child = new ChildKernel(kernel);
child.Get<Func<string>>()().Should().Be("foo");
child.Bind<Func<string>>().ToConstant(() => "bar");
child.Get<Func<string>>()().Should().Be("bar"); // this works fine
kernel.Get<SomeClass>().Name.Should().Be("foo");
child.Get<SomeClass>().Name.Should().Be("bar"); // is still foo
}
class SomeClass
{
private string _name;
public SomeClass(Func<string > getName)
{
_name = getName();
}
public string Name
{
get { return _name; }
}
}
Unresolvable instances such as SomeClass in this case are passed to the parent. Which means you have to tell the child how to resolve SomeClass by adding a binding,
Hi Remo,
Hmm, that's unexpected. In Unity Child Containers it would resolve the type from the child container using the child container dependency. Consider this scenario where I use child container to override its parents bindings:
public class DefaultNodeFactory : INodeFactory
{
private readonly IKernel _kernel;
public DefaultNodeFactory(IKernel kernel)
{
_kernel = kernel;
ConfigureContainer();
}
private void ConfigureContainer()
{
var nodeId = 1;
_kernel.Bind<Func<INode,string>>().ToConstant(n=>new NodeName(n.GetType()+nodeId++));
_kernel.Bind<INodeFactory>().ToConstant(this).InSingletonScope();
_kernel.Bind<INode>().To<Node>();
}
public T CreateNode<T>() where T : INode
{
return _kernel.Get<T>();
}
public T CreateNode<T>(string nodeName) where T : INode
{
using(var child = new ChildKernel(_kernel))
{
child.Bind<Func<INode,string>>().ToConstant(n=>nodeName); //override default name generator
erturn child.Get<T>();
}
}
}
At the point where I override,I really only want to override the node name generator. Unity would use that
override. Do you feel strongly about this behaviour? Is this something I could do easily in my own implementation of child kernels?
There is so much conceptionally wrong in this example:
- Use modules to define the bindings. That way no one has to create the factory and Ninject can do it instead.
- I guess from Node that you are building some data structure liker a list or a tree. Such data structures shouldn't be built using an IoC container normally.
- This is service locator like usage of Ninject.
- That's not what child kernel are thought for. Basically you are creating a new factory for several millions of $ just to build one car, tear it down afterwards to build a new factory for the next car. That's not very efficient, isn't it? In Ninject you should use conditional bindings for such things.
Finally, adding Bind().ToSelf() to your child kernel does what you want.
Wow, that's a bit presumptuous of you, no? I mean you don't really know the context of what we're doing.
- The INodeFactory in this case acts as a common interface and abstracts away the IOC container. It IS the container for all intents and purposes. The idea is for our users of this model to not be tied to an IOC container, so users can plug their own into our framework. Much like Prism does. See we're taking our time to add Ninject support next to Unity and Castle Windsor and others.
- Any object model is a tree. Objects that have dependencies on services can have them injected. Our nodes need services (name generators, child node factories, default property services, soap services , etc...) injected when they are constructed at runtime, so we clearly need IOC container functionality.
- This class is a common adapter to IOC containers. It would inherently need to use the container as a service locator. See point 1.
- I'm sorry, I'm not building a million $ factory, I'm instantiating a class that overrides its parent by composition. If that's inefficient, then OO is inefficient. :)
Now to the relevant points:
5. Bind().ToSelf() might actually override something we do not want to override if the T is actually already bound in the parent container. At the point of override we don't know wether a binding for the requested type exists, we only know that we want to override a particular service. And as I said, you don't need to do this in other child container implementation. Why should we do this in this one? Is this a ninject fundamental design difference over other implementations? It's an honest question which you haven't answered. Really, no offense intended.
You're saying overriding parent bindings is not a child kernel's responsibility? That's interesting. I thought that was the point of them. So how would you override a binding in Ninject without using child kernels? Constructor arguments are obviously out of the question because they are bound to magic string parameter names.
-
I still insist in this point no matter what system is behind. It's better to have a DefaultNodeFactoryModule providing the bindings and instead of using .ToConstant(this) you should use To<DefaultNodeFactory>.
-
You may create one class from your view. But behind this single class is a complex system of many other classes. Normally, you should keep it for a while and not create a new kernel foreach resolve.
-
The reason Ninject is different from Unity is that we feel that the Unity implementation has a huge design flaw:
public interface IFoo { } public class Foo1 : IFoo { } public class Foo2 : IFoo { } public class Bar { public Bar(IFoo foo) { this.Foo = foo; } public IFoo Foo { get; set; } } IUnityContainer parentCtr = new UnityContainer(); IUnityContainer childCtr1 = parentCtr.CreateChildContainer(); IUnityContainer childCtr2 = parentCtr.CreateChildContainer(); parentCtr.RegisterType<Bar, Bar>(new ContainerControlledLifetimeManager()); childCtr1.RegisterType<IFoo, Foo1>(); childCtr2.RegisterType<IFoo, Foo2>(); var bar1 = childCtr1.Resolve<Bar>(); var bar2 = childCtr2.Resolve<Bar>();
Using this example bar2.Foo will by type of Foo1. This means you can access instances created by one child container form another one. This is why we do not allow the parent container to use any instance from the child container when it creates an instance. The direction is one way in Ninject by intension. Children can access the parent but not the other way round.
What we can defenately improve though is that implicit bindings (bindings that are automatically created if no explicit binding exists) are created by the child kernel rather than the root parent.
Just a quick and dirty hack of what I would do:
var kernel = new StandardKernel();
kernel.Bind<Func<string>>().ToConstant(() => "Default");
kernel.Bind<Func<string>>()
.ToMethod(ctx => () => ctx.Parameters.OfType<NameParameter>().Single().Name)
.When(r => r.Parameters.OfType<NameParameter>().Any());
var name1 = kernel.Get<SomeClass>().Name;
var name2 = kernel.Get<SomeClass>(new NameParameter("SomeCustomName")).Name;
public void Test()
{
var kernel = new StandardKernel();
kernel.Bind<Func<string>>().ToConstant(() => "Default");
kernel.Bind<Func<string>>()
.ToMethod(GetGetNameFunction)
.When(r => r.Parameters.OfType<NameParameter>().Any());
var name1 = kernel.Get<SomeClass>().Name;
var name2 = kernel.Get<SomeClass>(new NameParameter("SomeCustomName")).Name;
}
private static Func<string> GetGetNameFunction(IContext ctx)
{
string name = ctx.Parameters.OfType<NameParameter>().Single().Name;
return () => name;
}
class NameParameter : Parameter
{
public NameParameter(string name)
: base("AbsolutelyIrrelevantName", (object)null, true)
{
this.Name = name;
}
public string Name { get; private set; }
}
Thank you for clarifying.
Is there something open or can this be closed?