Convention based binding of constructor string arg

2020-07-08 07:40发布

问题:

I'm using Ninject as IoC container in my project. I have following class:

public class SomeRepository:ISomeRepository
{
    public SomeRepository(string someDatabaseConnectionString)
    {
        // some code here..
    }
}

In my application settings file I have connection string named "someDatabase". By default the one should add following configuration in order to inject this connection string into the constructor:

kernel.Bind<ISomeRepository>()
    .To<SomeRepository>()
    .WithConstructorArgument("someDatabaseConnectionString", connString);

But i want to implement conventional based binding of such strings. Values for all constructor parameters of string type that names ends with "ConnectionString" should be taken from application's connectionStrings configuration section and injected automatically. I want to implement similar convention for appSettings section as well. This approach described in greater details in Mark Seeman's "Primitive Dependencies" article ("Conventions for primitives" section). Castle Windsor container was used in examples.

Is it possible to implement such conventions using Ninject and what is the best way to do this? I have already tried ninject.extensions.conventions but seems it doesn't has such a functionality, does it?

回答1:

It doesn't look like that kind of convention-based bindings is possible with Ninject right now. I had a similar question here and the suggestion was to make an interface to return the connection string and have that as the parameter. That could be tedious for many different connection strings though.

This is just a thought, but could you have an IConnectionStringProvider<T> that could use reflection to get the name of T and look up the application setting that way? Maybe like this:

public class ConnectionStringProvider<T> : IConnectionStringProvider<T>
{
    public string Value
    {
        // use reflection to get name of T
        // look up connection string based on the name
        // return the connection string
    }
}
...
public class SomeRepository:ISomeRepository
{
    public SomeRepository(IConnectionStringProvider<SomeRepository> connectionStringProvider)
    {
        this.connectionString = connectionStringProvider.Value;
    }
}

Also if that doesn't work, you could have a non-generic IConnectionStringProvider that takes a type as the argument:

public class ConnectionStringProvider : IConnectionStringProvider
{
    public string GetValueFor(Type type)
    {
        // use reflection to get name of type
        // look up connection string based on the name
        // return the connection string
    }
}
...
public class SomeRepository:ISomeRepository
{
    public SomeRepository(IConnectionStringProvider connectionStringProvider)
    {
        this.connectionString = connectionStringProvider.GetValueFor(this.GetType());
    }
}

If one of these works then they would have the advantage that they should work with any DI container.