我有一个使用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文件中定义。
请帮忙!谢谢!
答案 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>