【发布时间】:2018-06-19 17:56:00
【问题描述】:
我有一个关于 Ninject 的问题,但在直接回答问题之前,我先解释一下一般情况。
我有一个名为 ITest__Business 的业务接口及其实现 Test__Business。该类依赖于 3 个接口:ITest__Repository、ITest2__Repository 和 IConnectionUtil。 ITest__Repository、ITest2__Repository 和 IConnectionUtil 接口具有使用默认名称(分别为 Test__Repository、Test2__Repository 和 ConnectionUtil)实现它们的类。
这些域类表示具有存储库类的依赖关系的业务实体和一个连接实用程序来处理数据库连接的打开和关闭。业务类同时依赖于存储库和连接工具。在业务中创建的 connectionutil 由 2 个存储库共享(因此业务和存储库都处理与 DB 的单个连接)。
这是前面提到的代码:
public interface ITest__Business {
TablaUno ManageTablaUno(TablaUno tablaUno);
IConnectionUtil ConnectionUtil {get; }
IConnectionUtil ConnectionUtil2 {get; }
}
public class Test__Business : ITest__Business {
private IConnectionUtil connUtil1;
private ITest__Repository repo1;
private ITest2__Repository repo2;
public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1, ITest2__Repository repo2) {
this.connUtil1 = connUtil1;
this.repo1 = repo1;
this.repo2 = repo2;
}
public TablaUno ManageTablaUno(TablaUno tablaUno){
using (var scope = new TransactionScope()) {
// Methods of repo1 and repo2 within transaction.
//...
}
}
public interface ITest__Repository {
IConnectionUtil ConnectionUtil { get; }
TablaUno ManageTablaUno(TablaUno tablaUno);
}
public class Test__Repository : BaseRepository, ITest__Repository {
public Test__Repository(IConnectionUtil connectionUtil) : base(connectionUtil) {
// The connectionUtil is passed to the base class to retrieve the DbConnection
}
public TablaUno ManageTablaUno(TablaUno tablaUno) {
// Invocation to stored procedure with the connection of the base class.
}
}
public interface ITest2__Repository {/*Metodos propios*/}
public class Test2__Repository : BaseRepository, ITest2__Repository {
// Logic similar to Test_Repository.
}
public interface IConnectionUtil {
DbConnection Connection { get; }
void Open();
void Close();
}
public class ConnectionUtil : IConnectionUtil, IDisposable {
private SqlConnection connection;
public ConnectionUtil(string connStringKey) {
var connString = WebConfigurationManager.ConnectionStrings[connStringKey].ConnectionString;
connection = new SqlConnection(connString);
}
public DbConnection Connection => connection;
public void Open() {
try {
connection.Open();
} catch (Exception ex) {
Debug.WriteLine($"Excepcion en ConnectionUtil.Open: {ex.Message}");
throw;
}
}
public void Close() {
try {
if (connection != null && connection.State == ConnectionState.Open) {
connection.Close();
}
} catch (Exception ex) {
Debug.WriteLine($"Excepcion en ConnectionUtil.Close: {ex.Message}");
throw;
}
}
/*Disposable Logic*/
}
还有我的 Ninject 模块配置:
public class ConsoleModule : NinjectModule {
public override void Load() {
Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InCallScope().WithConstructorArgument("connStringKey", "Test1Connection");
Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
Kernel.Bind<ITest2__Repository>().To<Test2__Repository>().InTransientScope();
Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>().InTransientScope();
Kernel.Bind<ITest__Business>().To<Test__Business>().InTransientScope();
}
}
随后,它被请求一个新的存储库(repo3)和一个新的connectionutil(connUtil2)被添加到业务中。在业务中创建的 connUtil2 与 connUtil1 不同(它与不同的 DB 有自己的连接),它必须与新的存储库(repo3)共享。这样可以实现与2个不同数据库的交互。
为了实现这一点,我创建了一个属性类 ConnectionAttribute,它有一个构造函数,用于设置将从 .config 文件中读取的连接字符串。这个属性必须用新的连接字符串“Test2Connection”添加到connUtil2和repo3,表明它们是相关的。一个repo1、repo2和connUtil1不加这个属性,这样在解决依赖关系的时候,如果这些target没有这个属性,就会使用原来的连接字符串“Test1Connection”。
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ConnectionAttribute : Attribute {
public string ConnectionString { get; private set; }
public ConnectionAttribute(string connectionString) {
ConnectionString = connectionString;
}
}
public class Test__Business : ITest__Business {
// Properties and methods are ignored for brevity.
public Test__Business(IConnectionUtil connUtil1, [Connection("Test2Connection")] IConnectionUtil connUtil2,
ITest__Repository repo1, ITest2__Repository repo2, [Connection("Test2Connection")] ITestDb2__Repository repo3) {
this.connUtil1 = connUtil1;
this.connUtil2 = connUtil2;
this.repo1 = repo1;
this.repo2 = repo2;
this.repo3 = repo3;
}
}
我还更新了ninject模块:
public class ConsoleModule : NinjectModule {
private IList<string> scopeList = new List<string>();
public ConsoleModule() {
foreach (ConnectionStringSettings connstr in WebConfigurationManager.ConnectionStrings) {
scopeList.Add(connstr.Name);
}
}
//public override void Load() {
// Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InCallScope().WithConstructorArgument("connStringKey", "Test1");
// Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
// Kernel.Bind<ITest2__Repository>().To<Test2__Repository>().InTransientScope();
// Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>().InTransientScope();
// Kernel.Bind<ITest__Business>().To<Test__Business>().InTransientScope();
//}
public override void Load() {
Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InScope(context => {
var scopeCadena = string.Empty;
if (context.Request.Target.Member.DeclaringType.Name.Contains("Repository")) {
var pr = context.Request.ParentRequest.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
var cadena = pr.Length == 0 ? "Test1" : ((ConnectionAttribute)pr[0]).ConnectionString;
scopeCadena = scopeList.Single(x => x == cadena);
}
if (context.Request.Target.Member.DeclaringType.Name.Contains("Business")) {
var attrs = context.Request.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
var cadena = attrs.Length == 0 ? "Test1" : ((ConnectionAttribute)attrs[0]).ConnectionString;
scopeCadena = scopeList.Single(x => x == cadena);
}
return scopeCadena;
}).WithConstructorArgument("connStringKey", context => {
var cadena = "Test1";
if (context.Request.Target.Member.DeclaringType.Name.Contains("Repository")) {
var pr = context.Request.ParentRequest.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
cadena = pr.Length == 0 ? "Test1" : ((ConnectionAttribute)pr[0]).ConnectionString;
}
if (context.Request.Target.Member.DeclaringType.Name.Contains("Business")) {
var attrs = context.Request.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
cadena = attrs.Length == 0 ? "Test1" : ((ConnectionAttribute)attrs[0]).ConnectionString;
}
return cadena;
});
Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>();
Kernel.Bind<ITest__Business>().To<Test__Business>();
}
这似乎工作正常,但问题是每次我调用Kernel.Get
我应该如何为这个新案例进行 ninject 配置? 请帮帮我。
============================================== =======================
更新
另一方面,虽然给出了我的问题的答案,但我很想验证如果添加新接口 ITestDb3__Repository 以连接第三个数据库是否有效。 更新我的业务类构造函数如下所示:
public Test__Business(IConnectionUtil connUtil1,
ITest__Repository repo1, ITest2__Repository repo2,
[Connection("Test2Connection")] IConnectionUtil connUtil2, [Connection("Test2Connection")] ITestDb2__Repository repo3,
[Connection("Test3Connection")] IConnectionUtil connUtil3, [Connection("Test3Connection")] ITestDb2__Repository repo4) {
this.connUtil1 = connUtil1;
this.connUtil2 = connUtil2;
this.connUtil3 = connUtil3;
this.repo1 = repo1;
this.repo2 = repo2;
this.repo3 = repo3;
this.repo3 = repo4;
}
应该发生的是必须有 3 个不同的 connectionUtils 和 repo4 共享相同的 connUtil3。但是对于这个新场景,connUtil3 等于 connUtil2,因为只有当它具有属性而不是它的值时,范围才有效。 对于这个新场景,ninject 配置如何?
============================================== =======================
更新 #2
我需要有一种方法将存储库与业务类中的 connectionutils 关联起来。
第一种情况是当业务需要连接到 2 个数据库并依赖 3 个存储库时,这些存储库在内部执行对存储过程的调用。 存储库不处理连接,但这是通过接收连接字符串的 IConnectionUtil 接口完成的。 对于这种情况,代码如下:
/**** Test__Business constructor signature ****/
public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1_1, ITest2__Repository repo1_2,
IConnectionUtil connUtil2, ITestDb2__Repository repo2_1) { /* ... */ }
/**** Test__Business creation statements (equivalent to Kernel.Get<ITest__Business>) ****/
// repository1_1 and repository1_2 share the same connUtil1.
IConnectionUtil connUtil1 = new ConnectionUtil("TestConnection1"); // Connection string of the 1st database.
ITest__Repository repository1_1 = new Test__Repository(connUtil1);
ITest2__Repository repository1_2 = new Test2__Repository(connUtil1);
// repository2_1 shares the same connUtil2.
IConnectionUtil connUtil2 = new ConnectionUtil("TestConnection2"); // Connection string of the 2nd database.
ITestDb2__Repository repository2_1 = new TestDb2__Repository(connUtil2);
ITest__Business business = new Test__Business(connUtil1, repository1_1, repository1_2, connUtil2, repository2_1);
第二种情况是如果要求修改业务以使其连接到第三个数据库,现在取决于新的 IConnectionUtil 和新的 使用此 connectionUtil 的存储库(假设开发了 2 个新的存储库接口来调用第三个新数据库的存储过程), 这样,业务构造函数将如下所示:
/**** Test__Business constructor signature ****/
public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1_1, ITest2__Repository repo1_2,
IConnectionUtil connUtil2, ITestDb2__Repository repo2_1,
IConnectionUtil connUtil3, ITestDb3_1__Repository repository3_1, ITestDb3_2__Repository repository3_2) { /* ... */}
/**** Test__Business creation statements (equivalent to Kernel.Get<ITest__Business>) ****/
// repository1_1 and repository1_2 share the same connUtil1.
IConnectionUtil connUtil1 = new ConnectionUtil("TestConnection1"); // Connection string of the 1st database.
ITest__Repository repository1_1 = new Test__Repository(connUtil1);
ITest2__Repository repository1_2 = new Test2__Repository(connUtil1);
// repository2_1 shares the same connUtil2.
IConnectionUtil connUtil2 = new ConnectionUtil("TestConnection2"); // Connection string of the 2nd database.
ITestDb2__Repository repository2_1 = new TestDb2__Repository(connUtil2);
// repository3_1 and repository3_2 share the same connUtil3.
IConnectionUtil connUtil3 = new ConnectionUtil("TestConnection3"); // Connection string of the 3rd database.
ITestDb3_1__Repository repository3_1 = new TestDb3_1__Repository(connUtil3);
ITestDb3_2__Repository repository3_2 = new TestDb3_1__Repository(connUtil3);
ITest__Business business = new Test__Business(connUtil1, repository1_1, repository1_2, connUtil2, repository2_1, connUtil3, repository3_1, repository3_2);
如果我需要向其他数据库添加新连接,依此类推。我需要一种方法来在业务构造函数中关联哪些存储库共享相同的 connectionutil。
注意:每个存储库和 ConnectionUtil 对于每个业务对象都是唯一的,因此对 Kernel.Get 的 2 次调用必须产生不同的业务对象, 存储库和 ConnectionUtils。
请帮帮我。
【问题讨论】:
标签: c# .net visual-studio ninject ninject-extensions