Get with Named parameter

977 views
Skip to first unread message

Simone Chiaretta

unread,
Mar 20, 2009, 11:02:09 AM3/20/09
to nin...@googlegroups.com
Today I was writing a generic service locator for my app, and I wanted to write it so that it could either work with named parameters and not.
If you are not familiar with it, it's a new feature of Ninject2.

if you register a service like

Bind<IMapper>().To<CarMapper>().Named("car");
Bind<IMapper>().To<BoatMapper>().Named("boat");

you can get the correct service based on the name with this

_kernel.Get<IMapper>("car");

In my application most of the services depend on the kind of vehicle, so my service locator always passed the type of vehicle to the Get method.

But some might not depend on the type of vehicle, but I don't want to change the locator when that is going to change, and I'd like to make the change only module definition.

for example, now another service is registered like this:

Bind<IQuoteService>().To<VB6QuoteService>();

and it is always the same.
But if I try to get it with the same line as before

_kernel.Get<IQuoteService>("car")

I get a null back.

I simply put the Get method inside my own Get<T> method

        private T Get<T> (string tipoBene) where T: class
        {
            return _kernel.Get<T>(tipoBene) ?? _kernel.Get<T>();
        }

But I was wondering if that is a common scenario and if that way of retrieving the implementation should be default one or not.
Or maybe adding a kind of

Bind<IQuoteService>().To<VB6QuoteService>().Named().Always() to make it more explicit

Simone

--
Simone Chiaretta
codeclimber.net.nz
Any sufficiently advanced technology is indistinguishable from magic
"Life is short, play hard"

Ian Davis

unread,
Mar 20, 2009, 11:28:24 AM3/20/09
to nin...@googlegroups.com

Does this not work in your case?

public class NamedInstanceTests
{
 [Fact]
 public void CanResolveInstances()
 {
  using ( IKernel kernel = new StandardKernel( new[]{new QuoteModule()} ) )
  {
  var instance = kernel.Get<IQuoteService>();
  var namedInstance = kernel.Get<IQuoteService>( "car" );
  Assert.NotNull( instance );
  Assert.NotNull(namedInstance);
  }
 }
}

public class QuoteModule : NinjectModule
{
 public override void Load()
 {
  Bind<IQuoteService>().To<VB6QuoteService>().Named("car");


  Bind<IQuoteService>().To<VB6QuoteService>();
 }
}

public interface IQuoteService
{
}

public class VB6QuoteService : IQuoteService
{
}

Or do you just want Ninject to return the default if the named resolution fails?  I think you can get that functionality with an extension easily enough:

public static class IKernenExtension
  {
  public static T GetNamedOrDefault<T>(this IKernel kernel, string name) where T : class
  {
  return kernel.Get<T>(name) ?? kernel.Get<T>();
  }
  }

-Ian

--
Ian Davis

Simone Chiaretta

unread,
Mar 20, 2009, 11:39:59 AM3/20/09
to nin...@googlegroups.com
The answer is the second one...
the non named if there is a name that doesn't exist.

anyway you bring a good point

there are two scenarios:

1 - I want to return a certain implementation all any named parameter passed

Bind<IQuoteService>().To<VB6QuoteService>().Always();

2 - I want to return the default if there is no specific implementation

Bind<IQuoteService>().To<CarQuoteService>("car");
Bind<IQuoteService>().To<BikeQuoteService>("bike");
Bind<IQuoteService>().To<VB6QuoteService>().Default();

so that the last one is return if I call
kernel.Get<IQuoteService>("boat");


Making this NamedOrDefault the default behavior might not be good for all the scenarios, as maybe some want to get the null is something doesn't match.

Well.. just rambling

Simo

Nate Kohari

unread,
Mar 20, 2009, 11:55:40 AM3/20/09
to nin...@googlegroups.com
Simo:

We talked about this on IM, but replying here for the purpose of the group. :)

The more I think about it, the more I think the behavior you're seeing is correct. If you do:

kernel.Get<IService>("foo");

You're asking Ninject for an instance of IService using the named binding "foo". If that binding doesn't exist, the proper result is that you get back nothing.

Maybe I don't fully understand the use case for this. Why are you passing in a name when you know the service is bound without names?


Thanks,
Nate

Simone Chiaretta

unread,
Mar 20, 2009, 12:11:41 PM3/20/09
to nin...@googlegroups.com
Yes, I know the behavior is correct :)

But maybe, implementing the NamedOrDefault behavior can be useful.
I see the benefit in this:

you have to use many different services based on the kind of think you are handling, so you register
Bind<IService>().To<FooService>("foo");
Bind<IService>().To<BarService>("bar");
Bind<IService>().To<FizzService>("fizz");

But then, you also have other 10 "things" that all need the another implementation.
I can either solve the problem by registering 10 new bindings to the same implementation

Bind<IService>().To<DeafultService>("buzz");
Bind<IService>().To<DeafultService>("blop");
Bind<IService>().To<DeafultService>("sbang");
Bind<IService>().To<DeafultService>("gulp");

but it would be nice to be able to say

Bind<IService>().To<DeafultService>().AsDefault();

That was not the reason why I was passing the name even if I did know that the it was not bound to a name.
The reason why I used it was that at this point of the application I don't know which service has different implementation based on the name.
So I always pass the name, and when the developers that really know how that thing works will configure the modules in the correct way, that could be just one implementation or many. The specs I have now are that all services I have could be different based on the type of vehicle (the name).

Simo

Dave

unread,
Mar 20, 2009, 1:42:41 PM3/20/09
to ninject
I have the same issue with a twist.

What I a binding like
kernel.Bind<IFoo>().To<MyFoo>().named("Bar");
is made and a process requests a a instance of IFoo. If you don't
provide the name (or isn't set using the named attribute) you a null
result back.

A already submitted an issue (http://bugs.ninject.org/projects/NINJECT/
issues/view/NINJECT-ISSUE-40) where I proposed a AsDefault() method
which would create an alias
kernel.Bind<IFoo>().To<MyFoo>(). When a GetAll is done alias shoudn't
be returned.

I have already submitted another issue (http://bugs.ninject.org/
projects/NINJECT/issues/view/NINJECT-ISSUE-39) when I reported a bug
that Get<IFoo>("") doesn't returns the same object as Get<IFoo>()
does.
Microsoft documention about the common service locator says it should.
A emtpy string should be handled if it were a null value. This
behavior also exists in StructureMap, Spring.net, MicroKernel, MEF,
etc.

About calling Get with a non-existing name. It obvious that here null
is returned because you asked for a certain implementation.

With regards,
Dave
Reply all
Reply to author
Forward
0 new messages