c# - 存储库模式是如何真正完成的?

标签 c# elasticsearch dependency-injection asp.net-core repository-pattern

我在我的 asp.net 核心 web api 中使用 Elasticsearch 。当涉及到存储库责任时,我不太明白界限在哪里。

这是我定义我的实现的方式:

public SearchRespository: ISearchRespository<Product>
{
   private ElasticClient _client

   public async Task<ISearchResponse<Product>> SearchAsync(ISearchRequest request)
    {
        var response = _client.SearchAsync<Product>(request);
        return await products;
    }        

    . . . // others
}        

在我的 Controller 中:

public SearchController : Controller
{
    private ISearchRespository _repo;

    public SearchController(ISearchRespository repo)
    {
       _repo = repo;
    }

    public async Task<IActionResult> Search()
    {
       // build my search request from Request.Query

       var response = await _client.SearchAsync(request);
       var model = new SearchModel
       {
           Products = response.Documents;
           Aggregations = response.Aggregations;
       }

       return Ok(model)
}

就目前而言, repo 正在按原样传递弹性响应。我的问题是我画的线对吗?如果我只是将 _client 移动到我的 Controller 或将构建请求和构造 model 移动到 _repo 会怎样?你们如何使您的存储库正确?

最佳答案

您使用 Elastic Search 的事实应该是一个实现细节,尤其是 Controller 不应该知道,因此您将其从 Controller 中抽象出来是绝对正确的。我经常查看 SOLID 原则以了解我是否在正确的轨道上。如果我们看一下 Dependency Inversion Principle , 你会看到它引导我们走向一种也被称为 Ports and Adapters 的风格,这基本上意味着外部工具的使用被抽象掉了(端口),并且在应用程序的边界上实现了一个 Adapter。连接到该第三方。

因此,从依赖倒置原则的角度来看,您的方向是正确的。

然而,人们对Martin Fowler's Repository Pattern的理解存在很多误解。正在努力解决。定义如下:

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

这里需要注意的是,存储库旨在供领域层使用。

然而,存储库模式存在很多误用,因为许多开发人员开始将其用作查询的分组结构。正如我所见,该存储库并不适用于系统中的所有查询;但仅适用于 需要的查询。这些查询支持域为系统上的突变做出决策。

然而,您的系统需要的大多数查询都不是这种查询。您的代码就是一个很好的例子,因为在这种情况下,您完全跳过域,只执行读取操作。

这是不适合存储库的东西。我们可以通过再次将其与 SOLID 原则进行比较来验证这一点。

假设我们有以下存储库接口(interface):

public interface IUserRepository
{
    User[] FindUsersBySearchText(string searchText, bool includeInactiveUsers);
    User[] GetUsersByRoles(string[] roles);
    UserInfo[] GetHighUsageUsers(int reqsPerDayThreshold);
    // More methods here
}

这是您将看到开发人员编写的典型存储库抽象。从 SOLID 原则的角度来看,这种抽象是有问题的,因为:

  • 违反了接口(interface)隔离原则,因为接口(interface)范围很广(有很多方法),并且这些接口(interface)的使用者被迫依赖于他们不使用的方法。
  • 违反了单一职责原则,因为存储库实现中的方法不是高度内聚的。唯一与这些方法相关的是它们属于同一概念或实体。
  • 该设计违反了开放/封闭原则,因为几乎每次向系统添加查询时,都需要更改现有接口(interface)及其实现。每个接口(interface)至少有两种实现:一种是真实实现,另一种是测试实现。

像这样的设计也会在开发过程中带来很多麻烦,因为很难应用横切关注点(如安全、审计、日志记录、缓存等)。

所以这不是 Repository 模式旨在解决的问题;这样的设计只是严重违反 SOLID。

这里的解决方案是在您的系统中单独对查询建模,完全不使用存储库。有很多关于此的文章,您可以阅读我对此的看法 here .

如果我看一下您的设计,它实际上与我在这里推广的设计有一些相似之处,因为您似乎有一个可以处理多种查询类型的通用查询方法。然而,查询消息(您的 ISearchRequest)似乎特定于 Elastic Search。正如依赖倒置原则所述,这是您应该努力避免的事情。

关于c# - 存储库模式是如何真正完成的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42307577/

相关文章:

unit-testing - 你如何测试依赖注入(inject)?

c# - 玩家位置的敌人射击

exception - 从连接到ElasticSearch Cluster的客户端节点进行搜索时获取SearchPhaseExecutionException

elasticsearch - 如何添加到不同的字段值并将其存储在 Elasticsearch python中的另一个字段中( Elasticsearch 字段操作)

elasticsearch - 如何在 Elasticsearch 中显示摘要?

Grails 依赖注入(inject)问题

java - 如何在测试方法中使用@Autowired

c# - Linq 中的 lambda/方法语法中的左外连接

c# - 具有多个条件和组合的 LINQ 方法

c# - 如何在 C# (Unity) 中设置对象的最大旋转