动态使用Ninject连接到不同的数据库

时间:2011-10-10 20:07:31

标签: asp.net-mvc ninject connection-string

我有一个使用Ninject连接到单个数据库的MVC应用程序。现在我需要支持多个数据库。目前,我的global.asax.cs文件对ninject具有以下定义:

    protected void Application_Start() 
    { 
        AreaRegistration.RegisterAllAreas(); 
        RegisterRoutes(RouteTable.Routes); 

        //Using DI for controllers - use the Ninject custom controller factor 
        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); // Repository config is defined in ninject controller 
    }

这就是我的Ninject控制器类的样子:

public class NinjectControllerFactory : DefaultControllerFactory 
{ 
    private IKernel kernel = new StandardKernel(new EriskServices()); 

    protected override IController GetControllerInstance(RequestContext context, Type controllerType) 
    { 
        if (controllerType == null) 
            return null; 
        return (IController)kernel.Get(controllerType); 
    } 

    private class EriskServices : NinjectModule 
    { 
        public override void Load() 
        { 
            Bind<IRisksRepository>().To<MySql_RisksRepository>() 
                .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["mydb1"].ConnectionString); 
        } 
    } 
}

我还有一个处理用户身份验证的登录页面。它通过LDAP完成,不需要数据库连接。

我的问题是:我可以在用户身份验证登录页面之后绑定ninject connectionString吗?用户将拥有他们想要连接的数据库的下拉列表,例如“mydb1”或“mydb2”或“mydb3”。每个连接字符串都将在web.config文件中定义。

请帮忙!谢谢!

1 个答案:

答案 0 :(得分:5)

不,你不能绑定“之后” - 首先,Web应用程序是无状态的,你无法控制事件的顺序,但更重要的是,Ninject模块定义了你的IoC容器,这种配置几乎在<在应用程序或请求生命周期中的其他其他内容。

如果您说用户会从下拉列表中选择此选项,则选择存储库的行为是应用程序逻辑的一部分,而不是IoC配置的一部分。解决这个问题的方法是创建一个工厂界面。实现可以是Ninject内核本身的一个薄包装器。

public interface IRisksRepositoryFactory()
{
    IRisksRepository GetRepository(string name);
    // Optional: add a GetRepositoryNames() method for populating dropdowns, etc.
}

public class NinjectRisksRepositoryFactory
{
    private readonly IKernel kernel;

    public NinjectRisksRepositoryFactory(IKernel kernel)
    {
        if (kernel == null)
            throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    public IRisksRepository GetRepository(string name)
    {
        return kernel.Get<IRisksRepository>(name);
    }
}

对于这个特定的实现,您需要确保使用命名绑定(尽管一般来说您也可以使用元数据系统),并明确地执行每个绑定:

Bind<IRisksRepository>()
    .To<MySqlRisksRepository>()
    .InRequestScope()
    .Named("mysql")
    .WithConstructorArgument("connectionString", GetConnectionString("mydb1"));
Bind<IRisksRepository>()
    .To<OracleRisksRepository>()
    .InRequestScope()
    .Named("oracle")
    .WithConstructorArgument("connectionString", GetConnectionString("ordb1"));
Bind<IRisksRepositoryFactory>()
    .To<NinjectRisksRepositoryFactory>();

也可以在不显式创建每个绑定的情况下执行此操作,特别是如果所有目标都是相同类型(即,您只有MySqlRisksRepository),方法是将连接字符串或相关符号作为参数传递给{ {1}}调用并绑定到一个上下文方法而不是一个类型 - 但我建议不要使用它,因为就DI而言,它实际上与当前游戏相关。

还有一件事:不要担心这类似于“服务定位器”的反模式,因为就是这样 - 一种表面上的相似之处。当对象需要能够动态创建依赖项时,建议在IoC容器周围创建专用工厂,因为它可以最大限度地减少单个类的内核暴露,如果切换到不同的框架,可以轻松替换它。 / p>