搜索
首页后端开发C++使用存储库甚至 ORM 进行代码中的数据访问

Data access in code, using repositories, even with ORMs

简介

在 .NET 世界中,访问数据库最常用的方法之一是使用实体框架 (EF),这是一种与语言语法紧密集成的对象关系映射器 (ORM)。使用 .NET 语言原生的语言集成查询 (LINQ),使数据访问感觉就像使用普通的 .NET 集合一样,而无需了解太多 SQL 知识。这有其优点和缺点,我将尽量不在这里咆哮。但它始终造成的问题之一是软件项目结构、抽象级别和最终单元测试的混乱。

这篇文章将尝试解释为什么存储库抽象总是有用的。请注意,许多人使用存储库作为抽象数据访问的术语,虽然也有与类似事物相关的存储库软件模式,但它不是同一件事。在这里,我将把存储库称为一系列抽象数据访问的实现细节的接口,并完全忽略设计模式。

历史

如果您意识到这一点,请随意跳过这一点,但我必须首先解决我们如何开始想到存储库的想法。

在史前时期,代码只是按原样编写,没有结构,一切都在做你想让它做或至少希望它做的事情。没有自动化测试,只有手动黑客和测试,直到它起作用。每个应用程序都是用现有的任何东西编写的,对硬件要求的关注比代码结构、重用或可读性更重要。这就是恐龙被杀死的原因!真实的事实。

慢慢地,模式开始出现。特别是对于业务应用程序,业务代码、数据持久性和用户界面之间存在明显的分离。这些被称为层,很快被分成不同的项目,不仅因为它们涵盖了不同的关注点,而且因为构建它们所需的技能特别不同。 UI 设计与代码逻辑工作非常不同,与 SQL 或任何用于持久数据的语言或系统也非常不同。

因此,业务和数据层的交互是通过抽象成接口和模型来完成的。作为商务舱,您不会要求表中的条目列表,您将需要复杂对象的过滤列表。数据层有责任访问持久保存的内容并将其映射到业务可以理解的内容。这些抽象开始被称为存储库。

在数据访问的较低层,CRUD 等模式很快就占据了主导地位:您定义了表等结构化持久性容器,并且可以创建、读取、更新或删除记录。在代码中,这种逻辑将被抽象为集合,例如列表、字典或数组。因此,目前还有一种观点认为存储库的行为应该像集合一样,甚至可能足够通用,除了实际的创建、读取、更新和删除之外没有其他方法。

但是,我强烈不同意。作为业务数据访问的抽象,它们应该尽可能远离数据访问模式,而不是根据业务需求进行建模。这是实体框架(尤其是许多其他 ORM)的思维方式开始与存储库的原始想法发生冲突的地方,最终呼吁永远不要将存储库与 EF 一起使用,称其为反模式。

更多层数

模型之间的父子关系会产生很多混乱。就像一个部门实体,里面有人员。部门存储库是否应该返回包含人员的模型?也许不是。那么,我们如何将存储库分成部门(没有人员)和人员,然后有一个单独的抽象来映射到业务模型?

当我们将业务层分成子层时,混乱实际上会增加。例如,大多数人所说的业务服务是仅将特定业务逻辑应用于特定类型的业务模型的抽象。假设您的应用程序与人一起工作,因此您有一个名为 Person 的模型。处理人员的类将是 PeopleService,它将通过 PeopleRepository 从持久层获取业务模型,但也可以执行其他操作,包括数据模型和业务模型之间的映射或仅与人员相关的特定工作,例如计算工资。然而,大多数业务逻辑使用多种类型的模型,因此服务最终成为存储库上的映射包装器,几乎没有额外的责任。

现在假设您正在使用 EF 访问数据。您必须声明一个 DbContext 类,其中包含映射到 SQL 表的实体集合。您可以使用 LINQ 来迭代、过滤和映射它们,这些数据会在后台有效地转换为 SQL 命令,并为您提供所需的内容,以及分层的父子结构。该转换还负责内部业务数据类型的映射,例如特定的枚举或奇怪的数据结构。那么为什么你甚至需要存储库,甚至可能需要服务?

我相信,虽然更多的抽象层似乎是毫无意义的开销,但它们增加了人们对项目的理解,并提高了变革的速度和质量。显然,这是一种平衡,我见过一些系统架构明显要求所有软件设计模式都在任何地方使用。抽象只有在提高代码可读性和关​​注点分离时才有用。

原因

EF 变得麻烦的环境之一是单元测试。 DbContext 是一个复杂的系统,具有大量依赖项,必须花费很大的精力手动模拟。因此,微软提出了一个想法:内存数据库提供程序。因此,为了测试任何内容,您只需使用内存数据库即可完成。

请注意,在 Microsoft 页面上,此测试方法现在标记为“不推荐”。另请注意,即使在这些示例中,EF 也是由存储库抽象的。

虽然内存数据库测试有效,但它们添加了几个不容易解决的问题:

  • 设置内存中的 DbContext 需要现有实体的所有依赖项
  • 为每个测试设置和启动内存数据库都很慢
  • 为了获得有效的数据库输出,您需要设置的内容远多于您想要原子测试的内容

因此,最终发生的情况是,人们在“帮助程序”方法中设置数据库中的所有内容,然后创建从这种难以理解且复杂的方法开始的测试,以测试甚至最小的功能。如果没有此设置,任何包含 EF 代码的代码都将无法测试。

因此使用存储库的原因之一是将测试抽象移至 DbContext 之上。现在您根本不需要数据库,只需要一个存储库模拟。然后使用真实数据库在集成测试中测试您的存储库本身。内存数据库非常接近真实数据库,但也略有不同。

另一个原因(我承认我在现实生活中很少看到它具有实际价值)是您可能想要改变访问数据的方式。也许你想换成NoSql,或者一些内存分布式缓存系统。或者,更有可能的是,您从一个数据库结构开始,也许是一个整体数据库,现在您想将其重构为具有不同表结构的多个数据库。让我立即告诉您,如果没有存储库,这是不可能的。

特定于实体框架,您获得的实体是映射到数据库的活动记录。您对一个实体进行更改并保存对另一个实体的更改,然后您突然也会在数据库中更新第一个实体。或者也许您没有,因为您没有包含某些内容,或者上下文已更改。

EF 的支持者总是将实体跟踪宣传为一件非常积极的事情。假设您从数据库中获取一个实体,然后执行一些业务,然后更新该实体并保存它。通过存储库,您将获取数据,然后开展业务,然后再次获取数据以执行一些更新。 EF 会将其保留在内存中,知道它在更改之前没有更新,因此它永远不会读取它两次。这是真的。他们描述了数据库的内存缓存,它以某种方式感知数据库更改并跟踪您从数据库处理的所有内容,除非另有指示,否则将数据库条目双向映射到复杂的 C# 实体并来回跟踪更改,同时深度嵌入在业务代码中。就我个人而言,我认为这种过多的责任和缺乏关注点分离比使用它所获得的任何性能更具破坏性。此外,通过一些初步的努力,所有这些功能仍然可以在存储库中抽象,甚至可以在存储库的另一层内存缓存中抽象,同时保持业务、缓存和数据访问之间的清晰边界。

事实上,所有这一切的实际困难在于确定应该有单独关注点的系统之间的边界。例如,通过将过滤逻辑移至数据库中的存储过程,可以获得大量性能,但这会失去所用算法的可测试性和可读性。相反,使用 EF 或其他一些机制将所有逻辑转移到代码中,性能较差,有时甚至不可行。或者数据实体成为业务实体的点在哪里(参见上面的部门和人员示例)?

也许最好的策略是首先定义这些边界,然后决定采用哪些技术和设计。

我的结论

我相信应该始终使用服务和存储库抽象,即使存储库在底层使用实体框架或其他 ORM。这一切都归结为关注点分离。我永远不会认为实体框架是一个有用的软件抽象,因为它带有如此多的包袱,因此存储库非常适合在代码中对其进行抽象。 EF 是一个有用的抽象,但用于数据库访问,而不是在软件中。

我的软件编写理念是,从应用程序需求开始,为这些需求创建组件,并使用接口抽象任何较低级别的功能。然后,您在下一个级别重复该过程,始终确保代码可读,并且不需要了解使用的组件或当前级别使用的组件。如果情况并非如此,那么您就已经很好地分离了关注点。因此,由于没有任何业务应用程序需要使用特定的数据库或 ORM,因此数据层抽象应该隐藏所有这些知识。

企业想要什么?过滤后的人员列表? var people = service.GetFilteredListOfPeople(filter);不多不少。并且服务方法只会 return mapPeople(repo.GetFilteredListOfPeople(mappedFilter));同样,不多不少。回购如何获取人员、拯救人员或执行其他任何操作不是服务关心的问题。您需要缓存,然后实现一些实现 IPeopleRepository 并依赖于 IPeopleRepository 的缓存机制。您想要映射,请实现正确的 IMapper 接口。等等。

我希望我在这篇文章中没有过于冗长。我特意保留了代码示例,因为这更多的是一个概念问题,而不是软件问题。实体框架可能是我在这里抱怨的大部分目标,但这适用于任何在小事情上神奇地帮助你,但却破坏了重要事情的系统。

希望有帮助!

以上是使用存储库甚至 ORM 进行代码中的数据访问的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
C在现代世界中:应用和行业C在现代世界中:应用和行业Apr 23, 2025 am 12:10 AM

C 在现代世界中的应用广泛且重要。1)在游戏开发中,C 因其高性能和多态性被广泛使用,如UnrealEngine和Unity。2)在金融交易系统中,C 的低延迟和高吞吐量使其成为首选,适用于高频交易和实时数据分析。

C XML库:比较和对比选项C XML库:比较和对比选项Apr 22, 2025 am 12:05 AM

C 中有四种常用的XML库:TinyXML-2、PugiXML、Xerces-C 和RapidXML。1.TinyXML-2适合资源有限的环境,轻量但功能有限。2.PugiXML快速且支持XPath查询,适用于复杂XML结构。3.Xerces-C 功能强大,支持DOM和SAX解析,适用于复杂处理。4.RapidXML专注于性能,解析速度极快,但不支持XPath查询。

C和XML:探索关系和支持C和XML:探索关系和支持Apr 21, 2025 am 12:02 AM

C 通过第三方库(如TinyXML、Pugixml、Xerces-C )与XML交互。1)使用库解析XML文件,将其转换为C 可处理的数据结构。2)生成XML时,将C 数据结构转换为XML格式。3)在实际应用中,XML常用于配置文件和数据交换,提升开发效率。

C#vs. C:了解关键差异和相似之处C#vs. C:了解关键差异和相似之处Apr 20, 2025 am 12:03 AM

C#和C 的主要区别在于语法、性能和应用场景。1)C#语法更简洁,支持垃圾回收,适用于.NET框架开发。2)C 性能更高,需手动管理内存,常用于系统编程和游戏开发。

C#与C:历史,进化和未来前景C#与C:历史,进化和未来前景Apr 19, 2025 am 12:07 AM

C#和C 的历史与演变各有特色,未来前景也不同。1.C 由BjarneStroustrup在1983年发明,旨在将面向对象编程引入C语言,其演变历程包括多次标准化,如C 11引入auto关键字和lambda表达式,C 20引入概念和协程,未来将专注于性能和系统级编程。2.C#由微软在2000年发布,结合C 和Java的优点,其演变注重简洁性和生产力,如C#2.0引入泛型,C#5.0引入异步编程,未来将专注于开发者的生产力和云计算。

C#vs. C:学习曲线和开发人员的经验C#vs. C:学习曲线和开发人员的经验Apr 18, 2025 am 12:13 AM

C#和C 的学习曲线和开发者体验有显着差异。 1)C#的学习曲线较平缓,适合快速开发和企业级应用。 2)C 的学习曲线较陡峭,适用于高性能和低级控制的场景。

C#vs. C:面向对象的编程和功能C#vs. C:面向对象的编程和功能Apr 17, 2025 am 12:02 AM

C#和C 在面向对象编程(OOP)中的实现方式和特性上有显着差异。 1)C#的类定义和语法更为简洁,支持如LINQ等高级特性。 2)C 提供更细粒度的控制,适用于系统编程和高性能需求。两者各有优势,选择应基于具体应用场景。

从XML到C:数据转换和操纵从XML到C:数据转换和操纵Apr 16, 2025 am 12:08 AM

从XML转换到C 并进行数据操作可以通过以下步骤实现:1)使用tinyxml2库解析XML文件,2)将数据映射到C 的数据结构中,3)使用C 标准库如std::vector进行数据操作。通过这些步骤,可以高效地处理和操作从XML转换过来的数据。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具