IOption 模式 - 单元测试和层传递
IOption pattern - unit testing and passing through layers
我有一些代码 (C# .Net Core WebAPI) 我希望进行单元测试但需要一些帮助,因为依赖关系对我来说有点奇怪。
该代码来自一些示例代码(我在网上找到的),用于使用 .Net Core WebAPI 访问 MongoDb,最初看起来还不错,直到现在..
DbContext 和 Repository 都具有相同的依赖性 - 而且 Repository 只是将它传递给 DbContext - 因为 Repository 实例化了 DbContext:
public class LogItemRepository : ILogItemRepository
{
private readonly DbContext _context = null;
public LogItemRepository(IOptions<DbSettings> settings)
{
_context = new DbContext(settings);
}
...
public class DbContext
{
private readonly IMongoDatabase _database = null;
public DbContext(IOptions<DbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.Database);
}
public IMongoCollection<LogItem> LogItemsCollection
{
get
{
return _database.GetCollection<LogItem>("LogItem");
}
}
}
}
我不熟悉 Options pattern,但快速阅读它看起来不错。但我不相信将子依赖项(选项)、父项依赖项(如上例所示)作为良好做法。
我是否应该创建一个接口 IDbContext,并将其用作存储库的依赖项?这就是我过去会做的 - 但不确定这是否会破坏选项模式。
我怀疑这是主观的,但我希望得到其他人的意见。
谢谢
蒂姆
虽然主要基于意见,但通常的做法是不在存储库的构造函数中实例化数据库上下文。这将存储库与上下文紧密耦合。按照您在 OP 中的说明注入抽象。
我在这里可能有些胡思乱想,但在提供的示例中仍然存在太多紧密耦合。
首先抽象上下文
public interface IDbContext {
IMongoCollection<LogItem> LogItemsCollection { get; }
}
并且 IMongoDatabase
是显式依赖
public class DbContext : IDbContext {
private readonly IMongoDatabase database = null;
public DbContext(IMongoDatabase database)
this.database = database;
}
public IMongoCollection<LogItem> LogItemsCollection {
get {
return database.GetCollection<LogItem>("LogItem");
}
}
}
使用组合根(启动)所需的任何选项配置服务。您甚至可以考虑将其封装在扩展方法中。
services.AddScoped<IMongoDatabase>(provider => {
var settings = provider.GetService<IOptions<DbSettings>>();
var client = new MongoClient(settings.Value.ConnectionString);
return client.GetDatabase(settings.Value.Database);
});
services.AddScoped<IDbContext, DbContext>();
services.AddScoped<ILogItemRepository, LogItemRepository>();
//...NOTE: Use the desired service lifetime. This is just an example
现在让存储库明确依赖于上下文抽象
public class LogItemRepository : ILogItemRepository {
private readonly IDbContext context = null;
public LogItemRepository(IDbContext context) {
this.context = context;
}
//...other code
}
所有层现在都解耦并明确说明它们的依赖关系,允许根据需要进行更多独立的单元测试。
我有一些代码 (C# .Net Core WebAPI) 我希望进行单元测试但需要一些帮助,因为依赖关系对我来说有点奇怪。
该代码来自一些示例代码(我在网上找到的),用于使用 .Net Core WebAPI 访问 MongoDb,最初看起来还不错,直到现在..
DbContext 和 Repository 都具有相同的依赖性 - 而且 Repository 只是将它传递给 DbContext - 因为 Repository 实例化了 DbContext:
public class LogItemRepository : ILogItemRepository
{
private readonly DbContext _context = null;
public LogItemRepository(IOptions<DbSettings> settings)
{
_context = new DbContext(settings);
}
...
public class DbContext
{
private readonly IMongoDatabase _database = null;
public DbContext(IOptions<DbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.Database);
}
public IMongoCollection<LogItem> LogItemsCollection
{
get
{
return _database.GetCollection<LogItem>("LogItem");
}
}
}
}
我不熟悉 Options pattern,但快速阅读它看起来不错。但我不相信将子依赖项(选项)、父项依赖项(如上例所示)作为良好做法。
我是否应该创建一个接口 IDbContext,并将其用作存储库的依赖项?这就是我过去会做的 - 但不确定这是否会破坏选项模式。
我怀疑这是主观的,但我希望得到其他人的意见。
谢谢 蒂姆
虽然主要基于意见,但通常的做法是不在存储库的构造函数中实例化数据库上下文。这将存储库与上下文紧密耦合。按照您在 OP 中的说明注入抽象。
我在这里可能有些胡思乱想,但在提供的示例中仍然存在太多紧密耦合。
首先抽象上下文
public interface IDbContext {
IMongoCollection<LogItem> LogItemsCollection { get; }
}
并且 IMongoDatabase
是显式依赖
public class DbContext : IDbContext {
private readonly IMongoDatabase database = null;
public DbContext(IMongoDatabase database)
this.database = database;
}
public IMongoCollection<LogItem> LogItemsCollection {
get {
return database.GetCollection<LogItem>("LogItem");
}
}
}
使用组合根(启动)所需的任何选项配置服务。您甚至可以考虑将其封装在扩展方法中。
services.AddScoped<IMongoDatabase>(provider => {
var settings = provider.GetService<IOptions<DbSettings>>();
var client = new MongoClient(settings.Value.ConnectionString);
return client.GetDatabase(settings.Value.Database);
});
services.AddScoped<IDbContext, DbContext>();
services.AddScoped<ILogItemRepository, LogItemRepository>();
//...NOTE: Use the desired service lifetime. This is just an example
现在让存储库明确依赖于上下文抽象
public class LogItemRepository : ILogItemRepository {
private readonly IDbContext context = null;
public LogItemRepository(IDbContext context) {
this.context = context;
}
//...other code
}
所有层现在都解耦并明确说明它们的依赖关系,允许根据需要进行更多独立的单元测试。