我在RavenDB项目中实现repository and service pattern时遇到了一些困难。主要问题是我的存储库界面应该是什么样子,因为在RavenDB中我使用了几个索引来查询。
假设我需要获取parentid等于1的所有项目。一种方法是使用IQueryable List()并获取所有文档,然后添加where子句以选择parentid等于1的项目。这似乎是一个坏主意,因为我不能在RavenDB中使用任何索引功能。所以另一种方法是在存储库中有类似这样的IEnumerable Find(字符串索引,Func谓词),但这似乎也是一个坏主意,因为它不够通用,并且要求我实现这个方法,如果我从RavenDB更改一个普通的SQL服务器。
那么我如何实现通用存储库,但仍然可以从RavenDB中获得索引的好处?
答案 0 :(得分:10)
首先,问一下为什么要使用存储库模式?
如果您想要使用该模式,因为您正在进行域驱动设计,那么正如另一个答案所指出的那样,您需要重新考虑查询的意图,并根据您的问题谈论它域 - 你可以开始围绕这个进行建模。
在这种情况下,规格可能是你的朋友,你应该研究它们。
但是,在继续我的回答之前,让我们暂时看一下你问题的一部分:
似乎是一个坏主意,因为它不够通用,并且要求我实现此方法,如果我将从RavenDB更改为常见的SQL服务器。
你会以错误的方式解决这个问题 - 试图让你的系统在这个级别上完全与持久性无关是在寻找麻烦 - 如果你尝试从查询本身隐藏数据存储的独特功能那么为什么要使用RavenDB呢?
我倾向于在简单的面向文档中使用的方法(IE,我就数据而言,这就是你看起来正在做的事情),就是从我的命令中分离我的查询。
问问自己,为什么要按父ID查询文档?它是否在页面上显示列表?你为什么要用文件来模拟这个呢?为什么不根据视图模型对此进行建模,并使用从RavenDB中检索此数据的最有效方法? (对索引进行查询(动态或其他)),将其粘贴在工厂中,该工厂采用“一些输入”并生成“输出”,如果您决定更改持久性存储,则可以更改这些工厂。 (我在我的ASP.NET MVC应用程序中更进了一步,并且有单个动作控制器,我不称它们为控制器,在大多数情况下从这些控制器进行查询)。
如果您想通过父ID实际提取文档以更新它们或在它们之间运行某些业务逻辑,可能您已将它们建模错误 - 写入操作通常只涉及更改单个文档,或者换句话说,您应该围绕事务边界对文档进行建模。
TL; DR
考虑一下你实际想要实现的目标 - 为什么要使用“存储库模式”或“服务模式” - 这些单词作为描述你可能最终会遇到的情景的方式存在应用程序围绕您的需求,作为表达某个对象角色的常用方式 - 而不是您需要将您的每一项功能都塞进去。
答案 1 :(得分:10)
答案 2 :(得分:8)
假设我需要获取所有项目 parentid等于1。
首先,不要以这种方式考虑您的数据访问需求。
你不要需要“获取parentid等于1 的所有项目”。它将有助于尝试以这种面向数据的方式思考。
您需要的是获取具有特定父级的所有项目。这是一个存在于您的问题空间(您的应用程序的域)中的概念。
使用外键和名为parentid的字段在数据库中对此进行建模的事实是实现细节。封装它,不要在整个应用程序中泄漏它。
一种方法是使用IQueryable List()然后获取所有文档 添加where子句以选择项目 parentid等于1.这 似乎是一个坏主意,因为我做不到 使用RavenDB中的任何索引功能。所以 另一种方法是拥有 像这样的东西,IEnumerable 查找(字符串索引,Func谓词) 存储库,但似乎也是如此 像一个坏主意,因为
这两个都是坏主意。您的建议是要求调用存储库或查询的代码了解您的架构。
为什么您的存储库的使用者应该关注或知道存在parentid字段?如果这种情况发生变化,如果问题空间中某个特定概念的定义发生变化,那么代码中需要更改多少个位置?
每个用特定父级获取项目的地方。
这很糟糕,它是封装的对立面。
我的观点是,您希望将查询建模为显式概念,而不是传递的lambda或字符串,并且全部使用。
您可以使用规范模式,存储库上的命名查询方法,查询对象模式等显式建模查询。
不是 足够通用,需要我 如果我愿意,实施这个方法 从RavenDB更改为常见的sql 服务器
嗯,Func
太泛型。再次考虑一下,为了使用这种查询方法,您的消费代码需要知道什么,您将直接将代码的上层绑定到数据库模式。
此外,如果您从一个存储引擎更改为另一个存储引擎,则无法避免重新实现查询,其中性能足以成为使用存储引擎特定辅助工具的因素(例如,Raven中的索引)。
答案 3 :(得分:5)
我实际上不鼓励您使用存储库模式。在大多数情况下,它过度架构并且实际上使代码更复杂。
Ayende最近为此发了一些帖子:
我建议只针对Raven的原生API。
如果您认为我的回答过于笼统,请列出您希望通过使用另一层抽象获得的一些好处,我们可以继续讨论。