何时使用存储库模式


20

我最近读到,将存储库模式与ORM结合使用不是一种好习惯。从我的理解来看,这是因为它们在SQL数据库上提供的抽象太泄漏而无法包含在模式中。

我对此有两个问题:

  1. 如果要转出ORM,该怎么办?如果您未在存储库中包含ORM特定代码,则您的应用程序中将包含该代码。

  2. 当不使用ORM且您正在使用ADO.NET进行数据访问并自己填充对象数据时,存储库模式仍然有效吗?

  3. 如果您使用ORM而不是存储库模式,那么将常用查询保留在哪里?将每个查询表示为一个类并具有某种查询工厂来创建实例是否明智?


1
1)由于它们的行为不同,您将永远无法交换ORM,即使它们都支持linq,它们的行为也足以使您的应用程序崩溃。如延迟加载行为,肮脏跟踪,鬼代理等。然而很高兴能够换出ORM为in-MEM实施测试..
罗杰·约翰逊

对于它的价值,我在这里对Repository / ORM的讨论是:stackoverflow.com/questions/13180501/…–
Eric King

您从哪里读到这是一种不良做法?
Dave Hillier 2013年

Answers:


3

1)是的,但是您多长时间切换一次ORM?
2)我会这样说,因为ORM上下文是一种存储库,并且隐藏了很多工作,涉及创建查询,检索数据,映射等。如果您不使用ORM,则该逻辑仍必须保留在某个地方。我不知道从严格意义上来说,它是否可以作为存储库模式...
3)我经常看到封装查询,但是通常用于测试/存根。除此之外,在跨应用程序的不同部分重用查询时,我会格外小心,因为那样的话,您可能会创建一个依赖项,该依赖项可能会更改n倍(n是您使用查询的位置数)。


1
1)您问错了问题。不管多久一次,只需要一次。给定可用的ORM选项数量,可能有一个比您已经在使用的选项更好的选项。现在的问题变成:当您这样做时会发生什么?存储库为您提供了一个很好的抽象。我去过那里,但愿我首先做了这样的抽象。
devnull

3
@ devnull我不同意。如果最多发生一次,我认为这是可以接受的风险。如果您害怕做出错误的选择:请在尝试之前进行更多尝试。从理论上讲,这样的抽象听起来不错,但是在实践中,您最终会重新创建ORM api的相当大一部分,这都是因为您可能最终有一天会在某个地方选择另一个。对我来说,这是浪费时间,多余的代码以及您自己必须维护和保证的更多代码。此外,切换ORM不应影响整个应用程序;学习放置域边界。
Stefan Billiet

ORM更改不是存储库的唯一原因。如果您需要在应用程序中引入[分布式]缓存-所有更改将在存储库中完成,并且BL不会由于数据访问层的更改而更改。
Valery 2013年

在大多数情况下,分布式缓存不会内置在ORM中吗?
user1450877 2013年

我认为这取决于ORM。您可以决定使用比NHibernate或EF轻的ORM。
Valery 2013年

2

1)如果要转出ORM,该怎么办,如果不将其包含在存储库中,则应用程序中将具有特定的ORM代码。

我还没到公司突然决定转换数据访问技术的位置。如果发生这种情况,将需要做一些工作。我倾向于通过接口抽象数据访问操作。存储库是解决此问题的一种方法。

然后,我将有一个不同的程序集来具体实现我的数据访问层。例如,我可能有:

Company.Product.DataCompany.Product.Data.EntityFramework程序集。第一个程序集将纯粹用于接口,而另一个程序集将是Entity Framework数据访问逻辑的具体实现。

2)当不使用ORM且您正在使用ADO.net进行数据访问并自己填充对象数据时,存储库模式是否仍然有效?

我认为由您决定哪种模式有效或无效。我在表示层中使用了存储库模式。要记住的一件事是,人们喜欢将责任扔到存储库中。在不知不觉中,您的存储库课程将是跳舞,唱歌和做各种事情。您要避免这种情况。

我看到一个存储库类是通过承担GetAll,GetById,Update和Delete职责而开始的,这很好。到项目完成时,同一个类已经拥有了数十种本不应该存在的方法(职责)。例如,GetByForename,GetBySurname,UpdateWithExclusions和各种疯狂的东西。

这是查询和命令起作用的地方。

3)如果您使用ORM而不是存储库模式,则在哪里保留常用查询。将每个查询表示为一个类并具有某种查询工厂来创建实例是否明智?

我认为使用查询和命令代替存储库是一个很好的主意。我执行以下操作:

  • 定义查询的接口。这将帮助您进行单元测试。例如public interface IGetProductsByCategoryQuery { ... }

  • 定义查询的具体实现。您将能够通过您选择的IoC框架注入这些内容。例如public class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

现在,我不再需要承担数十种责任来污染存储库,而只是将查询分组到命名空间中。例如,上述查询的接口可能位于:Company.SolutionName.Products.Queries而实现可能位于Company.SolutionName.Products.Queries.Implementation

在更新或删除数据时,我以相同的方式使用命令模式。

有人可能会不同意,并说在项目完成之前,您将拥有数十个类和名称空间。是的你将会。在我看来,这是一件好事,因为您可以浏览所选IDE中的解决方案,并立即查看某些组件承担什么样的责任。如果您决定改用存储库模式,则必须查看每个存储库类以尝试确定其职责。


我喜欢用命令代替通用函数的想法。在哪里可以找到有关如何在数据访问上下文中实现它们的更多信息?
ankush981

1

免责声明:以下内容基于我对上述模式(即存储库)的理解和简要经验。我可能做错了...事实上,我很肯定自己做错了:)。因此,尽管这是一个答案,但它也是一个变相的问题。

我正在使用“存储库”模式来抽象化数据访问层,该数据访问层在大多数情况下是ORM。到目前为止,它是一个通用接口,具有用于LINQ to SQL和EF的Create,Read,Update,Delete和实现类的方法。我可以实现一个写入磁盘上XML文件的实现(这只是我可以用它做的一个疯狂的例子)。我没有实现工作单元,因为它受ORM支持。如果需要,我可能可以实现它。我喜欢这种方式,因为到目前为止,它给了我很好的分离。我并不是说没有更好的选择。

要回答您的问题:

  1. 无论您是否喜欢,都会发生变化。而且,如果您编写了许多应用程序,并且维护它们的时机已到,但是觉得使用当前的ORM并希望对其进行更改是很痛苦的,那么您会赞扬自己做出这样的抽象。只需使用您感到满意的任何东西,无论是存储库还是其他模式/概念。
  2. 是的,只要您使用它来分隔数据访问即可。就像我之前提到的,您可以实现将数据写入平面文件的实现。
  3. 当我有要调整的查询(数量不多)时,我使用存储库保留常用查询和查询对象

再举一个例子,Umbraco的员工已经摘除了DI容器,以防万一他们可能想要切换到其他容器。


0

如果ORM提供LINQ的灵活性,渴望加载等。我不会将其隐藏在任何额外的层后面。
如果涉及原始sql(微型ORM),则无论如何都应使用“每个查询的方法”以实现可重用性,从而使存储库模式非常合适。

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

为什么需要切换?
应该使用具有大多数所需功能的软件。新版的ormX可能会带来新功能,并且可能会比当前功能更好。但是...
如果选择隐藏orm,则只能使用所有候选者都具有的功能。
例如,您无法Dictionary<string, Entity>在实体中使用属性,因为ormY无法处理这些属性。

假设使用LINQ,则大多数orm开关只是切换库引用并替换session.Query<Foo>()context.Foos或类似,直到编译为止。无聊的任务,但是比对抽象层进行编码所需的时间更少。

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

是。代码应该是可重用的,这意味着将sql构建,对象实现等放在一个位置(单独的类)。您也可以调用类“ XRepository”并从中提取接口。

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

假设使用LINQ,那么类包装器将是恕我直言。更好的方法是扩展方法

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

在多个位置(或可能)使用且足够复杂(容易出错)的任何代码都应提取到集中位置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.