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

使用存储库甚至 ORM 进行代码中的数据访问

Dec 25, 2024 am 11:21 AM

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

热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

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

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1662
14
CakePHP 教程
1418
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
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和XML的未来:新兴趋势和技术 C和XML的未来:新兴趋势和技术 Apr 10, 2025 am 09:28 AM

C 和XML的未来发展趋势分别为:1)C 将通过C 20和C 23标准引入模块、概念和协程等新特性,提升编程效率和安全性;2)XML将继续在数据交换和配置文件中占据重要地位,但会面临JSON和YAML的挑战,并朝着更简洁和易解析的方向发展,如XMLSchema1.1和XPath3.1的改进。

继续使用C:耐力的原因 继续使用C:耐力的原因 Apr 11, 2025 am 12:02 AM

C 持续使用的理由包括其高性能、广泛应用和不断演进的特性。1)高效性能:通过直接操作内存和硬件,C 在系统编程和高性能计算中表现出色。2)广泛应用:在游戏开发、嵌入式系统等领域大放异彩。3)不断演进:自1983年发布以来,C 持续增加新特性,保持其竞争力。

C多线程和并发:掌握并行编程 C多线程和并发:掌握并行编程 Apr 08, 2025 am 12:10 AM

C 多线程和并发编程的核心概念包括线程的创建与管理、同步与互斥、条件变量、线程池、异步编程、常见错误与调试技巧以及性能优化与最佳实践。1)创建线程使用std::thread类,示例展示了如何创建并等待线程完成。2)同步与互斥使用std::mutex和std::lock_guard保护共享资源,避免数据竞争。3)条件变量通过std::condition_variable实现线程间的通信和同步。4)线程池示例展示了如何使用ThreadPool类并行处理任务,提高效率。5)异步编程使用std::as

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深度潜水:掌握记忆管理,指针和模板 C深度潜水:掌握记忆管理,指针和模板 Apr 07, 2025 am 12:11 AM

C 的内存管理、指针和模板是核心特性。1.内存管理通过new和delete手动分配和释放内存,需注意堆和栈的区别。2.指针允许直接操作内存地址,使用需谨慎,智能指针可简化管理。3.模板实现泛型编程,提高代码重用性和灵活性,需理解类型推导和特化。

C社区:资源,支持和发展 C社区:资源,支持和发展 Apr 13, 2025 am 12:01 AM

C 学习者和开发者可以从StackOverflow、Reddit的r/cpp社区、Coursera和edX的课程、GitHub上的开源项目、专业咨询服务以及CppCon等会议中获得资源和支持。1.StackOverflow提供技术问题的解答;2.Reddit的r/cpp社区分享最新资讯;3.Coursera和edX提供正式的C 课程;4.GitHub上的开源项目如LLVM和Boost提升技能;5.专业咨询服务如JetBrains和Perforce提供技术支持;6.CppCon等会议有助于职业

现代C设计模式:构建可扩展和可维护的软件 现代C设计模式:构建可扩展和可维护的软件 Apr 09, 2025 am 12:06 AM

现代C 设计模式利用C 11及以后的新特性实现,帮助构建更灵活、高效的软件。1)使用lambda表达式和std::function简化观察者模式。2)通过移动语义和完美转发优化性能。3)智能指针确保类型安全和资源管理。

See all articles