c# – 在ASP.NET MVC 3 Web App Ninject EF中为不同用户设置不同的连接字符串

我试图弄清楚如何在每个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实现限制为单个请求.我是对还是不对?

最佳答案 解决方案是不直接注入连接字符串,而是注入某种类型的IConnectionFactory并将开关(Request.Url.Host)代码移动到工厂实现.这简化了您的注册,无需实现ControllerBase.

您的问题类似于我刚刚回答的this question.

UPDATE

实现这一点的方法取决于您的应用程序,其设计和要求,但您可以在应用程序的核心层中定义以下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而不是字符串.

通过这种方式,您的服务不知道如何为自己创建连接,并且您已经将其抽象出来了.这使得稍后重构和更改代码变得更容易.

您甚至可能希望更进一步,并将DbContext或IDbContextFactory注入您的存储库.

点赞