我们在具有多个数据库的多租户Web环境中使用Ninject.MVC5和Ninject.Extention.Conventions,每个租户一个,以及一个主EF数据库。当用户登录时,我们在主数据库中找到它们并确定它们应该使用哪个数据库,这样我们就可以将所有datacontexts绑定到该数据库。 (我们使用EF作为主数据库,使用Linq to SQL作为租户数据库)。
这是初始绑定:
private static void RegisterServices(IKernel kernel)
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
其中TennantConnection最初是虚拟默认连接字符串
这是使用更新的连接字符串
登录后调用的Rebindkernel.Rebind<TenantDB>().ToSelf().InRequestScope().WithConstructorArgument(typeof(string), ConfigConnection);
将内核注入rebind类的构造函数中,如下所示:
public DataContextTennant(IKernel kernel)
所有其他注射均按惯例完成。
问题在于,当我们部署网站(它恰好是Azure云应用程序)时,许多用户在首次登录后会收到无效SQL连接错误,我认为这是由于重新绑定造成的。但是如果他们使用私有浏览器会话,则重新绑定似乎对该会话和后续会话都有效。
答案 0 :(得分:0)
我建议Ninject按预期工作。您应该在登录完成并重新绑定之前查看为什么TenantDB
已实例化。这应该是导致你的问题的原因。
为此,您应该首先删除默认的TenantDB
绑定:
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
因为毕竟这个绑定只会导致TenantDB
无法使用,所以为什么要费心呢?它只是稍后将问题后期处理 - 使其更难检测。
fail fast 更好 - 让ninject抛出ActivationException
! (如果没有约束就会发生。)
这可以帮助您找出在登录完成之前TenantDB
实例化的情况。
修改:验证IBindingRoot.Rebind
有效:
public class Test
{
[Fact]
public void Foo()
{
const string text1 = "Text1";
const string text2 = "Text2";
var kernel = new StandardKernel();
kernel.Bind<string>().ToConstant(text1);
kernel.Get<string>().Should().Be(text1);
kernel.Rebind<string>().ToConstant(text2);
kernel.Get<string>().Should().Be(text2);
}
}
Ninject的重新绑定工作。我认为你犯了一个配置错误,当你使用ninject时,你的对象树的构建部分 - 这取决于DbContext
之前的Rebind
。因此,“登录前DbContext
”会在登录后泄漏。
如果我错了,你应该创建一个minimal verifiable example并将其发布在Ninject的问题跟踪器上。
答案 1 :(得分:0)
虽然我无法使用Ninject解决它,但我能够通过Unity解决问题。在控制器中,在检索租户连接字符串后,我将其称为:
public static void RegisterDBTypes(IUnityContainer container, string connection)
{
container.RegisterType<ADataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<RDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<PDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
}
这会重新绑定流向各种服务和存储库的数据上下文。