Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject conditional binding based on parameter type

I'm using a factory to return a datasender:

Bind<IDataSenderFactory>()
    .ToFactory();

public interface IDataSenderFactory
{
    IDataSender CreateDataSender(Connection connection);
}

I have two different implementations of datasender (WCF and remoting) which take different types:

public abstract class Connection
{
    public string ServerName { get; set; }
}

public class WcfConnection : Connection
{
    // specificProperties etc.
}

public class RemotingConnection : Connection
{
    // specificProperties etc.
}

I am trying to use Ninject to bind these specific types of datasender based on the type of Connection passed from the parameter. I have tried the following unsuccessfully:

Bind<IDataSender>()
    .To<RemotingDataSender>()
    .When(a => a.Parameters.Single(b => b.Name == "connection") as RemotingConnection != null)

I believe this is because '.When' only provides a request and I would need the full context to be able to retrieve the actual parameter value and check its type. I'm at a loss as to what to do, other than using named bindings, actually implementing the factory and putting the logic in there i.e.

public IDataSender CreateDataSender(Connection connection)
{
    if (connection.GetType() == typeof(WcfConnection))
    {
        return resolutionRoot.Get<IDataSender>("wcfdatasender", new ConstructorArgument("connection", connection));
    }

    return resolutionRoot.Get<IDataSender>("remotingdatasender", new ConstructorArgument("connection", connection));
}
like image 690
Nathan Marlor Avatar asked Mar 07 '13 09:03

Nathan Marlor


1 Answers

After some looking into Ninject source I have found following:

  • a.Parameters.Single(b => b.Name == "connection") gives you variable of type IParameter, not real parameter.

  • IParameter has method object GetValue(IContext context, ITarget target) that requires not null context parameter (target can be null).

  • I have not found any way to get IContext from Request (variable a in your sample).

  • Context class does not have parameterless constructor so we can't create new Context.

To make it work you can create dummy IContext implementation like:

public class DummyContext : IContext
{
    public IKernel Kernel { get; private set; }
    public IRequest Request { get; private set; }
    public IBinding Binding { get; private set; }
    public IPlan Plan { get; set; }
    public ICollection<IParameter> Parameters { get; private set; }
    public Type[] GenericArguments { get; private set; }
    public bool HasInferredGenericArguments { get; private set; }
    public IProvider GetProvider() { return null; }
    public object GetScope() { return null; }
    public object Resolve() { return null; }
}

and than use it

kernel.Bind<IDataSender>()
      .To<RemotingDataSender>()
      .When( a => a.Parameters
                   .Single( b => b.Name == "connection" )
                   .GetValue( new DummyContext(), a.Target ) 
               as RemotingConnection != null );

It would be nice if someone could post some info about obtaining Context from inside When()...

like image 67
Davor Avatar answered Oct 20 '22 09:10

Davor