views:

505

answers:

2

Ok Dependency Ninja's, here's one for you...

I want to change the way Ninject builds a dependency based upon the type I'm requesting from the Kernel.

I have a DbConnectionFactory class with the following constructors:

    public DbConnectionFactory()
        : this(MyDatabase.ConnectionString)
    {
    }

    public DbConnectionFactory(string connectionString)
        : this(DbProviderFactories.GetFactory("System.Data.SqlClient"), connectionString)
    {
    }

For the "default" binding, I want Ninject to use the parameterless constructor:

        this.Bind<IDbConnectionFactory>().To<DbConnectionFactory>();

Certain classes in my code need Ninject to supply the connectionString paramter. I've tried to setup the binding like so:

        this.Bind<IDbConnectionFactory>().To<DbConnectionFactory>().Only(
            When.Context.InstanceOf(typeof(IRepository))).WithArgument(
            "connectionString", MyOtherDatabase.ConnectionString);

However, I only ever get Ninject to use the default constructor.

I must be missing something obvious!

+2  A: 

It sounds as if there is a bit too many layers here. Why do you need a DatabaseGateway? Why are you not using the ADO.NET provider factories to create connections (you can wire these up through Ninject as well) to manage your connections directly? OR better yet, with the repository pattern use something like NHibernate and use this to map objects??

Regardless, how about always using the longer constructor and alternately passing in either MyDatabase.ConnectionString or MyOtherDatabase.ConnectionString depending on the situation? You could use a provider as described here and here but I try to avoid them unless absolutely necessary:

  1. Define two new attributes

    public class DefaultDatabaseAttribute : Attribute {} public class OtherDatabaseAttribute : Attribute {}

  2. Decorate the DatabaseGateway arguments with the appropriate attributes in the constructor

    public class OneRepository : IRepository { public OneRepository([DefaultDatabase]DatabaseGateway factory) { } }

    public class TwoRepository : IRepository { public TwoRepository([OtherDatabase]DatabaseGateway factory) { } }

and your provider would look like:

public class DatabaseGatewayProvider : SimpleProvider<DatabaseGateway>
{
        protected override DatabaseGateway CreateInstance(Ninject.Core.Activation.IContext context)
        {
            IDbConnectionFactory factory;
            if (context.Member.IsDefined(typeof(DefaultDatabaseAttribute), false))
            {
                factory = context.Kernel.get<IDbConnectionFactory>(With.Parameters.ConstructorArgument("connectionString", MyDatabase.ConnectionString)));
            }
            else if (context.Member.IsDefined(typeof(OtherDatabaseAttribute), false))
            {
                factory = context.Kernel.get<IDbConnectionFactory>(With.Parameters.ConstructorArgument("connectionString", MyOtherDatabase.ConnectionString)));
            }

            return new DatabaseGateway(factory);
        }
}

You can expand this model to add additional attributes to decided if the factory even needs to be returned in the DatabaseGateway.

It is awkward but may be the best you can do given the number of layers involved.

Jeffrey Cameron
+1  A: 

This article may help you out as well. Like Jeff said, you'll want to use the With.Parameters.ConstructorArgument(). You can pass in multiple arguments by continuing to chain ConstructorArgument or by using a Dictionary. Hope this helps!

J.R. Garcia