4

我试图弄清楚如何在每个 Web 请求上设置不同的连接字符串。

这是我的场景:

  • 一个基于 asp.net mvc 3 + ninject + 实体框架的 webapp
  • 单一实体模型
  • 具有相同模型的三个物理上不同的数据库

到目前为止,我尝试做的是通过覆盖基本控制器的 OnActionExecuting 方法来设置默认控制器工厂(传递正确的连接字符串)(每个控制器都继承自自定义基)但是如果我尝试使用两个应该同时访问不同数据库的不同用户不起作用。似乎第一个用户设置了他的连接字符串,第二个用户选择了已经设置的连接字符串,而没有设置新的默认控制器。

这是一些代码...

// 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);
        }
    }
 }

如您所见,我正在使用 .InRequestScope() 将 ninject 实现限制为单个请求。我是对还是错?

4

1 回答 1

2

解决方案是直接注入连接字符串,而是注入IConnectionFactory某种类型的连接字符串并将switch (Request.Url.Host)代码移动到工厂实现。这简化了您的注册并消除了实施ControllerBase.

您的问题与我刚刚回答的这个问题相似。

更新

实现这一点的方式取决于您的应用程序、其设计和要求,但您可以例如IConnectionFactory在应用程序的核心层中定义以下内容:

public interface IConnectionFactory
{
    SqlConnection OpenConnection();
}

由于数据库的选择特定于 ASP.NET 的使用,因此您必须在 MVC 项目中定义此接口的实现。它可能如下所示:

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;
    }
}

您可以按如下方式注册:

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

您的SqlUsersRepository(和所有其他服务)现在可以依赖于IConnectionFactory而不是string.

这样,您的服务就不必知道如何为它们自己创建连接,并且您已经将其抽象掉了。这使得以后重构和更改代码更容易。

您甚至可能想更进一步,将DbContextorIDbContextFactory注入到您的存储库中。

于 2013-05-21T11:36:24.583 回答