3
votes

I am using .NET 4.5, Ninject 3 with the binding by convention lib as follow:

kernel.Bind(x => x
    .FromAssembliesMatching("assembly.dll")
    .SelectAllClasses().InheritedFrom(typeof(ICommandHandler<>))
    .BindAllInterfaces());

And this is binding properly for:

public class MyCommandHandler : ICommandHandler<MyCommand>

But doesn't bind:

public class MyGenericCommandHandler<T> : ICommandHandler<MyGenericCommand<T>>

However, the previous binding works if I add individual bindings for specific implementation of my generics class, such as:

kernel.Bind(typeof(ICommandHandler<MyGenericCommand<float>>))
      .To(typeof(MyGenericCommandHandler<float>))
kernel.Bind(typeof(ICommandHandler<MyGenericCommand<int>>))
      .To(typeof(MyGenericCommandHandler<int>))

But adding each individual generic type defeats the purpose of conventions and require adding binding for each possible individual type such as float, int, string, etc...

Do you know how to modify the convention or add another one (or even come with a completely different solution) to support the generic version of my command? i.e. supporting two-level generics.

1
It doesn't sound likke you're doing anything wrong or crazy, buty it's not clear to me what you want. An open generic class can't be instantiated, but you can do stuff like this. Can you clarify what TParameter is, how you're specifying the Type you want, and it's no harm to put in more of the exception.Ruben Bartelink
@RubenBartelink I did improve the question and gave a more specific example. I hope this answers your question.Adam
Do you have a class that derives from [and hence implements] ICommandHandler<MyGenericCommand<float>> coz the message says you don't ? Is it public? Is the open generic with the type necessary? Typically people have one level of open generic - the ICommandHandler<> and then have a concrete class implementing that. Unfortunately I'm more confused now but hopefully someone else can see what you're trying to doRuben Bartelink
@RubenBartelink, I did simplify the question and thank you for trying to help. I removed redundant explanation.Adam
@Adam: Are you hooked to Ninject, or do you mind if I answer your question with how to do this with Simple Injector?Steven

1 Answers

0
votes

EDIT: Doesn't compile [and the fact it doesn't reveals the requirement doesn't make sense], see reasoning for this conclusion in the comments.


This is just the normal open generics case. As alluded to with a link in my earlier comment, the way a basic bind to make that DRY looks is simply:

kernel.Bind(typeof(ICommandHandler<MyGenericCommand<>>))
    .To(typeof(MyGenericCommandHandler<>));

This binding will then suffice for any T variant of ICommandHandler<MyGenericCommand<T>>.

In the context of doing convention based binding it to mapping what you're expecting, the problem is that SelectAllClasses().InheritedFrom(typeof(ICommandHandler<>)) does not include generic classes -- this makes sense as it's rare that you can specify generalized rules on non concrete classes.

You can either

  • use a different Projection portion of the DSL to select the Generic classes and then bind them in some convention based manner
  • expose a NinjectModule from assemblies exposing [open] generic services which does the above Bind and then do a kernel.Load() of the module [prob by the DLL name pattern].