4

I'm trying to figure out how to set up a different connection string upon each web request.

This is my scenario:

  • one single webapp based on asp.net mvc 3 + ninject + entity framework
  • one single entity model
  • three phisically different databases with the same model

What I've tried to do so far is to set the default controller factory (passing the right connection string) by overriding the OnActionExecuting method of the base controller (every controller is inherited from a custom base) but if I try to access with two different users that should access different databases at the same time it doesn't work. It seems that the first user set his connection string and the second one pick the one it's already set without setting a new default controller.

Here's some code...

// Base controller

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var database = String.Empty;
        switch (Request.Url.Host)
        {
            case "aaa.domain.local":
                database = "AAA";
                break;
            case "bbb.domain.local":
                database = "BBB";
                break;
            case "ccc.domain.local":
                database = "CCC";
                break;
        }

        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(WebConfigurationManager.ConnectionStrings[database].ConnectionString));
    }

}

// Ninject controller factory

public class NinjectControllerFactory : DefaultControllerFactory
{
    private IKernel _kernel;

    public NinjectControllerFactory()
    {
    }

    public NinjectControllerFactory(string connectionString)
    {
        _kernel = new StandardKernel(new ABCServices(connectionString));
    }

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
            return null;
        return (IController)_kernel.Get(controllerType);
    }

    private class ABCServices : NinjectModule
    {
        private string _connectionString;

        public ABCServices(string connectionString)
        {
            _connectionString = connectionString;
        }

        public override void Load()
        {
            Bind<IUsersRepository>().
                To<SqlUsersRepository>().
                InRequestScope().
                WithConstructorArgument("connectionString", _connectionString);
        }
    }
 }

As you can see I'm using .InRequestScope() to limit the ninject implementation to the single request. Am I right or not?

1 Answer 1

2

The solution is to NOT inject a connection string directly, but inject an IConnectionFactory of some sort and move the switch (Request.Url.Host) code to the factory implementation. This simplifies your registration and removes the need for implementing a ControllerBase.

Your problem is similar to this question that I just answered.

UPDATE

The way to implement this depends a bit on your application, its design and its requirements, but you could for instance define the following IConnectionFactory in the core layer of your application:

public interface IConnectionFactory
{
    SqlConnection OpenConnection();
}

Since the selection of the database is specific to the use of ASP.NET, you will have to define an implementation of this interface in your MVC project. It might look as follows:

public class AspNetHostDatabaseConnectionFactory 
    : IConnectionFactory
{
    public SqlConnection OpenConnection()
    {
        string connStr = GetConnectionString();

        var connection = new SqlConnection(connStr);

        connection.Open();

        return connection;
    }

    // This is the old code from your OnActionExecuting method.
    private static string GetConnectionString()
    {
        var database = String.Empty;

        switch (HttpContext.Current.Request.Url.Host)
        {
            case "aaa.domain.local":
                database = "AAA";
                break;
            case "bbb.domain.local":
                database = "BBB";
                break;
            case "ccc.domain.local":
                database = "CCC";
                break;
        }

        return WebConfigurationManager
            .ConnectionStrings[database].ConnectionString;
    }
}

You can register it as follows:

kernel.Bind<IConnectionFactory>()
    .To<AspNetHostDatabaseConnectionFactory>();

Your SqlUsersRepository (and all other services) can now depend on the IConnectionFactory instead of a string.

This way your services don't know have to know how to create a connection for them selves and you've abstracted this away. This makes refactoring and changing your code later on easier.

You might even want to go a step further and and inject a DbContext or IDbContextFactory into your repositories.

Sign up to request clarification or add additional context in comments.

3 Comments

good suggestion. I'm relatively new to Ninject and IoC so do you mind suggesting me some tutorials about it where I can see an implementation for the connection factory?a@Steven, good suggestion. I'm relatively new to Ninject and IoC so would you mind suggesting me some tutorials about it where I can see an implementation for the connection factory?
@user2401097: No, I can't. I don't think there's really much tutorials that write about connection factories themselves. I updated my question with an example though and you should definitely read Mark Seemann's book about Dependency Injection in .NET.
I'm looking for similar feature, but I'm not using Injection. Any direct solution? Also I should be able to change connection string in different controllers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.