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
}

所有层现在都解耦并明确说明它们的依赖关系,允许根据需要进行更多独立的单元测试。