我们应该在什么时候选择nHibernate而不是其他ORM



我承认,我没有完全进入休眠状态。

既然像Dapper这样的微ORM可以用来满足大多数数据访问需求,那么需要像nHibernate这样的大炮的场景是什么?有哪些例子可以说明nHibernate会发光?需要明确的是,我不认为"在不更改代码的情况下切换数据库的能力"有太大优势。在八年的编程生涯中,我从未真正做到过这一点,而且一开始这似乎是一个浪费时间的想法。

我愿意接受任何深思熟虑的回应,但以下是我的一些问题示例:

  1. 与Dapper这样的查询相比,查询API什么时候值得在映射中投入额外的工作
  2. 如何利用延迟加载来限制开发工作并发挥作用
  3. 什么时候值得花时间研究如何批处理语句
  4. 在什么情况下,缓存系统比页面输出缓存更好?这只适用于非分布式环境吗
  5. 像我这样的凡人怎么会理解nHibernate在分布式环境中的工作方式?考虑混合缓存、批处理和无状态会话,并考虑负载平衡的web服务器将如何处理所有这些

哇,大问题。不确定一个凡人能回答这个问题。但我认为你太快了,不能忽视"切换数据库的能力"。有很多软件包,包括商业软件包和开源软件包,都提供了将不同的RDBMS作为后备存储的能力。管理部署到2+数据库平台的SQL可能会成为一场噩梦,因此让某些东西以可预测的方式构建SQL(至少与手工编写相比)是一个巨大的优势。对于一些数据库平台,我可以看到事务吞吐量的增长,这使得数据库选择的成本也高得令人望而却步。大多数ORM都会以这样或那样的方式帮助您实现这一点——尽管当数据库需求足够复杂时,拥有丰富的查询API会有很大帮助。

我认为简单的答案是,当你的数据库对应用程序的需求达到一定的复杂程度时,在没有的情况下满足你的需求的成本会低于nhibernate学习曲线所涉及的成本。我不能提供完整的答案,但我会试着说出我对你的清单项目的想法。

  1. 当你做的不仅仅是CRUD。在多个数据库平台上需要复杂的查询可能就是一个很好的例子。在这种类型的应用程序上,你几乎可以维护两个独立的代码库(好吧,如果你走存储的proc路线,它们真的会分离),并且将所有代码保存在.net中是有价值的(例如,能够用其他代码对这些查询进行单元测试是很好的)
  2. 除了在中等信任环境中看到的问题之外,我不确定延迟加载现在不"正常工作"。在我看来,懒惰加载的唯一问题是,你需要意识到它,以避免在获取大量数据时可能出现的一些问题,主要是N+1选择问题
  3. 你不需要弄清楚如何批处理语句-你只需要设置一个配置值,然后忘记它。这是NHibernate用最少的精力为你做的一个非常巨大的优化-当它只直接涉及操作和事务控制时,你的代码可以干净得多
  4. 当您为不同的用户以不同的方式呈现页面,或者在域层中进行任何类型的非琐碎处理时,缓存返回的数据都是有益的。即使在基本场景中,使用页面输出缓存,您最终也可能在缓存中拥有编辑页面、详细信息页面等,而在离源更近的地方缓存数据,您只需要缓存实体一次。离源更近的缓存还可以为您提供更多保护,使您免受陈旧数据的影响。面向数据的缓存也可以通过服务或通过将nHibernate指向进程外存储(如memcached或redis)在多个应用程序之间共享。这在某些环境中可能非常有价值
  5. 我不确定你是否需要了解它是如何工作的(很多时候我使用开源库来保护自己不需要了解这类东西的实现细节)。但简单的答案是,在分布式场景中,除了缓存(只有二级缓存)之外,这些都没有任何不同的行为。只要您使用分布式缓存提供程序(或将所有服务器指向同一进程外缓存提供程序),您就应该在这方面做得很好

我只是在说nHibernate,但我想Hibernate的故事基本上是一样的。对于规模更大、更复杂的应用程序,可能会有很多好处,但为了获得这些好处,您还需要承担很多额外的复杂性——它可能还不如推出自己的解决方案来解决所有问题复杂*Hibernate为您解决。

关于缓存,您似乎也有很多问题。我建议阅读这个链接,了解一级和二级缓存是如何工作的。我不会在这里解释,因为听起来你的理解比我在这个已经很长的回复中所能理解的要深刻:)

NHibernate是一个强大的系统,但你不必了解它的所有信息就可以成功使用它。要回答你的问题:

  1. 据我所知,所有的.NET微ORMS都不支持LINQ,而是依赖于在代码中混合SQL字符串。使用LINQ构建查询为您提供了类型安全性、编译时检查和强大的重构能力。如果所有查询都使用SQL字符串,请尝试重构包含数千个查询的代码库。。。诶呀所谓重构,我指的是添加新列、新表等简单的事情,这是企业环境中经常发生的事情。重构字符串是可能的,这是依赖存储过程的人仍然必须做的事情,但如果我有类型安全性,我肯定不会选择这样做。

  2. 对于延迟加载,您唯一需要记住的就是创建一个SELECT N+1场景。每当代码在域对象/实体上执行foreach循环时,请确保填充对象的查询使用了.Fetch()方法,该方法只需在SQL中创建一个JOIN并填充任何子对象。否则,当您在对象上进行foreach并点入任何子对象时,ORM将不得不执行另一个SELECT语句来"获取"数据。基本上,用NHibernate的行话来说,热切吸引是你的朋友。

  3. 配料就像用NHibernate做馅饼一样简单。在你的NHibernate配置中,打开批处理,就这样。之后,如果你确实需要,你可以在运行时为特定的查询调整批处理大小。

  4. 我自己从来没有使用过二级缓存。我在一个大企业环境中工作,我们的应用程序运行非常快,不需要任何缓存。NHibernate中的一级缓存不需要配置,可以简单地认为是更改跟踪。基本上,NHibernate在内部保留一个字典,其中列出了它已经从数据库中检索到的对象,以及哪些对象待保存/更新在数据库中。一级缓存是我从未真正考虑过的东西,但我想最好能在基本层面上了解它的工作原理。

  5. 同样,我目前在一个企业环境中工作,我们有各种各样的应用程序使用NHibernate;一些非常基本,另一些则使用了NHibernate所提供的所有强大功能。然而,根据我的经验,我通常看到的是,并不是团队中的每个成员都需要成为NHibernate专家。通常,1-3名开发人员将非常了解情况,其他人也不会担心,只需创建实体、映射并继续编程。一旦基础设施到位,并且您的组织已经整理出了希望使用的模式,一切通常都很简单。

其他想法:

NHibernate真正闪光的地方是它能够映射任何类型的疯狂数据库设计。现在我并不是说映射90年代初的一些疯狂数据库设计会很容易,在那里你必须将一个存储过程和另一个表连接在一起,但是可能的。我这一天做了一些疯狂的映射。有些我甚至认为是不可能的,因为数据库的设计并不是为了做我们想做的事情,但每次,只要有了持久性,我仍然能够凭借NHibernate在映射好的和坏的数据库设计方面令人难以置信的灵活性来实现它。

使用Micro ORMS,您通常会在代码中嵌入大量SQL字符串。这怎么被认为是干净和高效的呢。现在,似乎所有人都在把他们用来放入存储过程的东西放入代码中。实际上,我在一些项目中确实使用了Micro ORM,尽管这是有意义的,但通常情况下,当我只在一个表上查询一些简单的数据时——没有复杂的where子句。

公平地说,我是那些花了相当多时间学习NHibernate来龙去脉的人之一,但不是因为我需要工作,而是因为我只是想。我在工作中与很多每天使用NHibernat的人一起工作,但他们并没有完全明白。但他们也不需要。你只需要知道一些基本的事情,你就可以去了。

希望这能有所帮助。

最新更新