Entity Framework 成功案例?

13

就像许多开发者之前一样,我面临着选择一种ORM进行深入学习并在未来的项目中推进的十字路口。我一直在尝试使用Entity Framework,并且到目前为止我很喜欢它,虽然我只尝试了一些简单的事情,如具有明确定义的表关系和一对一的表实体映射的CRUD操作。 我在Google上搜索了一些关于Entity Framework的信息,发现大多数是负面反馈...最让我担心的是映射xml和生成的对象类的复杂性...以及它们依赖于EF的组件类,因此在涉及对象模型设计考虑时需要予以尊重。目前,我倾向于选择NHibernate...因为它的映射文件相对简单,并且可以与我自己设计的POCO对象一起工作以满足我的对象模型设计需求。甚至有一个LINQ提供程序,以及第三方代码生成工具。 不过...我还不想放弃EF。有没有人知道任何用EF编写的成功生产应用?或者在技术方面,有没有任何理由要现在偏向EF,例如拥有企业支持,是新的ORM因此更有可能随着时间推移而增加功能集合,而NH已经达到了成熟的平衡点? 我不是想在这里引发争论,我只是在寻找人们可能想要添加有关EF的积极事物,以便做出决定。

谢谢!

5个回答

14

虽然我倾向于同意Tim关于框架选择中“宗教”元素的观点,但我仍然觉得很奇怪,为什么这么多人愿意对EF发表评论,明显他们根本不知道它是如何工作的。事实证明,EF只是NHibernate的糟糕版本,就像锤子不能当螺丝刀一样。重要的是要了解EF的本质。

在我回答这里的一些评论提出的问题之前,我想补充一句,我们刚刚将一个生产Web应用程序的第二个版本发布给客户,其中0行SQL,100%所有DB访问都通过EF或ASP.NET成员资格API完成。我在这里说话具有真实的使用EF的经验,遗憾的是,这显然不适用于我迄今为止看到的大多数EF评论的作者。

总的来说,我认为扩展实体类型是一个错误。那篇Tim引用的博客文章的作者既不知道这是可能的,也认为这是实施DDD的途径,这告诉我有关他实际使用EF的经验的所有信息:他没有任何经验

对于某些ORM的用户来说,POCO支持变得非常重要,因为多年来它们没有功能完备的LINQ实现。由于您基本上被限制使用黑匣子中出现的任何类型对象,所以对父类型的控制变得非常重要。事实上,将您的“POCOs”编写为 每个成员 声明为public virtual 开始被视为小事一件事情。这是哪个星球上的“普通”类型?这是一个代理基类,而不是POCO。所谓的“POCO”根本不是POCO。只是他们更愿意在封装和性能方面做出妥协,而不是在父类型上妥协。在他们的工具集限制范围内,这可能是一个合理的妥协,但并没有什么值得骄傲的。

但 EF 的工作方式不同,通过 LINQ 投影可以和实际映射的类型一样轻松地实现任意类型的具体化。如果您正在构建要将对象通过网络传输而不是内部使用,则必须有不能依赖于 EF 的客户端,那么就可以使用 RIA 服务或编写 ADO.NET 数据服务。所有的辛苦工作都将为您完成。EF 是处理将数据库中的数据投影到各种类型客户端的一系列工具的基础,它不是一个单独的、独立的工具,试图在一个庞大的框架中处理应用程序可能需要的每一个数据转换。让 EF 处理从关系型数据库到对象空间的投影。例如,您可以使用 LINQ to Entities 将其投影到持久性无关类型上,或者使用 RIA 服务将其投影到 Silverlight 友好的线路格式上。要求一个工具处理应用程序可能需要的所有投影是请求使用这个工具被困在某个狭小的领域里。
EF 基于“值对象”的概念。这些是具有属性但没有行为的类型。结果发现,值对象非常适用于某些特定的问题,如在网络上传递数据或位于关系型数据库与面向对象编程之间的“空白地带”。重要的是要理解这里的责任分离:实体类型的目的是将关系型数据库数据转换为对象空间。然后,您可以编写业务类型来实现应用程序,并将实体类型投影到业务类型上。这样,您就可以自由地实现业务类型,而不需要对持久性做任何妥协。并且在需要时可以更改 RDB 模式。您只需更改实体映射和 BO 投影而不是 BO 本身。

1
真实世界的经验是我一直在寻找的; 最终,将实体类与行为类分开可能是有意义的,尽管在阅读了Rockford Lhotka的CSLA框架并编写了自己的“ORM”后,我对逻辑封装的自包含性感到非常依赖。看起来EF最适合面向服务的应用程序,其中应用程序逻辑位于服务上,但我仍然需要将数据对象发送到客户端... EF实体完美地适合此,并且可以由逻辑对象服务端发送/接收。 - user157627
所谓的“POCOs”根本不是POCOs。如果你仔细想想,非虚拟属性也不是简单的,你可以在里面放置行为,即你可以在里面放置代码 :-) 属性是ORM消费者将智能放入模型的机制,虚拟属性是ORM制造商将智能放入其ORM提供的机制。从NH中加载(使用session.Load<entityHere>(idHere))对象非常智能,没有性能损失,当您要在多对多关系中使用它时,它只使用存根(不会访问数据库)。 - Hao
只有当您访问NHibernate存根对象的属性而不是其ID时,它才会访问数据库。对于一些未经培训的EF用户,他们倾向于使用开箱即用的内容,他们会倾向于使用db.Ent.Find,认为在EF上没有使用存根的机制,但实际上是有的,但这是一个麻烦的世界,如果您想要访问真正的对象,您需要分离存根对象。 - Hao
将EF实体映射到自定义实体基本上是放弃了EF的所有功能,如延迟加载、脏数据跟踪、标识映射等。例如,如果您要求自己的自定义UoW获取BO对象之一,则需要一个ID映射。如果您更改BO对象上的属性,则可能需要进行一些有趣的脏数据跟踪以更新底层EF对象...我并不是说这种方法是错误的,对于复杂的模型,我也会这样做,但其中存在一些痛苦...在许多情况下,仅使用部分类扩展EF对象并将其用于您的域即可。 - Roger Johansson
我从未费心去学习它的工作原理。我曾经费心去学习它的工作原理,但我发现我必须调整其他所有东西来服务于EF,而不是EF为我服务。如果我遇到性能问题,“哦,你需要学习EF的工作原理”。胡说八道。EF很慢,在许多场景下只是一个糟糕的选择。我向后端专家寻求帮助,他推荐使用EF,但他无法轻松地查看我的代码并告诉我如何正确地做。他只是给了一些没有帮助的一般性建议。然后我发现他使用专门的EF开发人员。什么?!如果你需要专门的EF开发人员,那么在我看来,EF就有问题。 - BVernon

5
我放弃了Entity Framework 4.1并因以下原因再也没有使用它:
  • 官方文档糟糕透顶。几乎没有关于如何以及为什么会出现这种情况的信息。除了基本的增删改查场景外,没有任何其他示例。例如,我有5个关联表,一个原子操作(事务)应该删除其中2个表的记录,然后向第三个表添加一条记录,最后将最后2个表更新为刚刚添加的表的ID。我可以清楚地看到执行此操作所需的SQL。但在Entity Framework中,通过对数据上下文进行哪些操作可以达到相同的结果仍是个谜。

有些热心人撰写了很好的文章和博客,他们深入了解其中的内容,但嘿,这不同于在参考书中查找,我为什么希望有人比官方来源更了解呢?为什么Microsoft不费心给其旗舰数据访问技术提供更好的手册,介绍超越他们一年级课本式样例的使用方法?缺乏关于如何使用它的信息使得学习曲线更陡峭,导致浪费时间和错过截止日期。

  • 这是一个非常完美的黑盒子,没有办法深入其中并查看它正在尝试做什么。尽管在倡导使用IoC和依赖注入,但微软团队很少在自己的产品中遵循这些原则。实体框架也不例外。大多数核心类都被标记为sealed(EntityConnection、MetadataWorkspace),因此您无法覆盖方法并查看内部情况。ToString()方法仅适用于查询。如果您想查看通过将POCO堆叠到数据上下文中并运行SaveChanges生成的真实SQL语句,则可以忘记它,没有办法做到这一点。具有讽刺意味的是,根本没有所谓的更改集概念,因此包含一系列修改的东西深藏在内部,从未向开发人员公开。我的意思是,当涉及到CUD操作时,没有任何可调用ToString()的内容。

  • “友好”的错误消息。几乎每个错误消息都是一个难题,平均需要30分钟的搜索和阅读沮丧的开发人员的讨论。为什么很难在该死的错误消息中放置一些有助于解决问题的信息?“从数据库获取提供程序信息时发生错误”比“未知错误”好在哪里?

  • 我感觉我无法控制正在发生的事情以及每个特定时刻的位置。实体可以进行延迟加载、缓存或根据显式请求从数据库中提取。而且我无法通过查看对象来确定它处于什么状态或来自何处。它是否过时、悬挂还是事件附加到上下文?当我将其链接到另一个实体时,Entity Framework会确保它仍然在数据库中吗?您所暴露的只是POCO对象、常规集合和DbContext中的一些附加方法。等等,IDbSet(类似ICollection的)接口是数据库能够执行的所有操作的超集吗?承认数据库略微复杂,因此需要专门的接口和其他对象来清晰地传达意图不是更公平吗?鉴于Entity Framework不仅仅是拉取和推送数据,那么拥有像Cached<TEntity>和LazyCollection<TEntity>这样的东西,在您查看它时告诉您故事,会更有意义吗?试图将ADO.NET压缩成ICollection和类似接口的形式,为简单性而牺牲了重要细节并不是一个明智的想法。它损害了理解的便利性,并为意外错误留下更多空间。我的意思是,对象模型需要紧密反映域。您不能忽略它或隐藏Database类的静态方法中的差异。

总的来说,我花了更多时间盲目尝试各种方法让它工作,而不是专注于真正的问题。我发现尽管ADO.NET需要更多的打字,但结果保证在有限(而且不是非常糟糕)的时间内得出,而使用实体框架可能会花费几天时间却一无所获。这是一个巨大的障碍。因此,底线是:实体框架不能帮助您解决业务问题。它只会让你在与另一个设计/文档不良的技术进行持续战斗中浪费时间,仅仅为了使用LINQ语法进行简单查询而感到愉悦。不要冒险浪费你的时间,这不值得。如果你有足够的时间,那就去试试吧,这是向客户开高账单的好借口。
更新:积极地说,在我的下一个项目中,我将尝试使用实体框架5.0。谢天谢地,这次我们有源代码 :) 所以现在看起来并不那么糟糕。

2
这种类型的问题存在一个问题,就是人们选择框架时会有一定的“宗教”因素,ORM激发了相当多的热情。
冒着引起争议的风险,我个人并不是Entity Framework的铁杆粉丝。我可以看出,在将遗留系统和ER模型映射时,它可能非常好用,但我认为从长远来看(以及我的使用案例),更多地建立在领域模型设计的基础上,并将我的实体映射到该模型上。这里有一篇很好的博客文章,我倾向于同意其中的观点。
我认为我喜欢NHibernate和Eco等产品所采用的方法。目前,我正在尝试使用真正的OODBMS(而不是ORM)与DB4Objects一起进行实验,并且到目前为止印象非常好。

我敢打赌,EF v4会让你更满意——支持真正的POCO、领域驱动设计等等。当前版本实际上是一个最好的妥协。 - marc_s
DB4O真是太棒了。在查询速度和开发时间方面的表现简直让人震惊。我还没有尝试过客户端/服务器架构,所以对此我不能做出评判,但是我相当自信。如果我的老板能像我一样兴奋就好了:P - Boris Callens

2
短答案:等到EF 4.0再使用。
长答案:
我现在正在完成一个中等规模的EF 1.0项目(也是ASP.NET MVC)。我还有使用NHibernate的经验。除了常见的CRUD映射外,我们还进行了相当多的继承建模。总体而言,我的感觉很复杂。
以下是一些观察:
我能够说关于EF 1.0最好的事情是它具有出色的LINQ集成。它比当前版本的Linq to NHibernate更好。
EF可以很好地处理有趣的继承映射,但是你要试图弄清楚它,需要处理非常多的映射错误。文档在这里并不是特别有用,社区支持也不够丰富。你只能自己想办法。
设计器存在漏洞,并且不支持EF可以做的所有内容。你经常会发现自己需要手动编辑XML映射文件。设计器会在本来正确的映射xml中创建错误。实际上,我们与设计器的问题非常多,所以我们会先进入XML,而不是让设计器更新映射。我认为这是我对Microsoft产品的普遍问题:他们构建面向工具而非库的产品。在这种情况下,他们匆忙推出了工具,因为XML同样糟糕。
XML太冗长了。EF 1.0需要大量的映射信息。与NHibernate的XML相比,后者只处理映射本身,并不需要概念定义或存储定义文件,或者更好的是像Fluent NHibernate这样的工具,它允许您在代码中指定映射。
我个人更喜欢编写可以自己持久化(POCO)的类,而不是处理单独的数据实体模型。尽管Craig提到的公共虚拟点,但我更喜欢NHibernate的代理方法。我对Craig关于EF意图的看法持有异议。我认为他们并不打算在EF和业务对象之间创建映射层。作为部分类,其目的似乎是您向实体添加行为,以创建完整的OO类。考虑到这一点,你不能轻易地进行单元测试EF 1.0。
也许最大的恼人之处是EF缺乏透明的惰性加载。那真的很糟糕。
我将等待EF 4.0发布后再使用它。

你因为试图直接使用实体类型而不是投影而感到烦恼,因为你没有懒加载。当你进行投影时,所有的加载都会自动处理,而不必急切地加载或显式调用Load。当然,逆流而上是很痛苦的。问题是,为什么你如此坚信我们这些在内胎上漂流的人做错了事,而你却在挣扎和咒骂呢? - Craig Stuntz
我即将在一个月后的免费在线CodeRage会议上发表演讲,如果您感兴趣,请收听。使用LINQ将实体映射到业务类型确实很容易,只是其他ORM的用户不习惯以这种方式工作。对于子类型,抱歉,您的观点是完全错误的;在LINQ中这很简单。我理解无法使用LINQ的开发人员可能会觉得这很困难,但他们只需要赶上就好了。 - Craig Stuntz
所以,你的观点是我的BO具有抽象订单和类型订单(CustomerOrder/EmployeeOrder)不同的属性?在你的例子中,项目是匿名类型,但我可能会使用类型化的BO,比如CustomerOrder或EmployeeOrder。现在我又回到了将行为类映射到值类的问题上 - 基本上是使用带有更改跟踪的内部状态对象。如果没有ORM/LINQ,我将传递数据到行为类的构造函数中:CustomerOrder(CusomterOrderInfo info)。现在我必须管理一个单独的、平行的状态对象图。这并不是漂浮的。 - pfries
将非匿名类型投影到与投影到匿名类型一样容易。在Entity框架中,“实体”具有明确而明显的含义,与EF外部的“实体”不同。如果您愿意,可以阅读“EntityObject的子类型”,但这很难适应长度限制的评论。映射类型(无论是“POCO”还是其他类型)与BO(关注点分离)不同;将它们视为相同的设计是不好的。是的,如果您投影到真正的 POCO(而不是代理基础),则更难跟踪更改,但我会为了清晰的关注点分离和应用程序中没有失控状态而支付这个代价。 - Craig Stuntz
Craig,我有机会观看了你的演讲,我想附上一个链接和一些想法。这个演讲(http://blogs.teamb.com/craigstuntz/2009/09/11/38394/)非常有趣,我真的很喜欢将业务对象投影到测试和OO封装中的含义。然而,我认为关于从POCOs映射回EDM的异议仍然存在。如果你的POCOs没有更改跟踪,你必须构建自定义映射到你的数据访问层。无论这是否是一个值得的权衡,任何遵循你方法的人都应该事先了解。 - pfries
显示剩余6条评论

1
我们在InterWorks已经使用NHibernate四年了。它对我们非常有用,我们还在其上构建了另一层来简化使用(类似于Castle ActiveRecord,当时我们不知道)。
NHibernate的缺点是学习曲线陡峭,缺乏强大的GUI工具(我们使用自己的MyGeneration模板生成对象),以及社区支持一般。我们花费了很多精力让它为我们工作,并花费了相当多的时间培训新团队成员关于NHibernate和我们围绕它构建的框架。
由于我们主要构建定制软件,很难为我们证明重建我们的框架的开销,但最有可能在未来两年内,我们将转向Entity Framework,原因有几个基本原因:(1)更好的文档和更大的支持社区(这将很快发生)和(2)更容易找到有经验的EF人员,从而减少一些培训成本。
这些今天并不是真实的,但随着VS.NET 2010中EF的下一个版本的发布,它们将很快变得真实。它已经非常成熟,在内置的VS.NET支持下正在逐渐完善。我们已经进行了一些基本测试,但我不敢声称对EF有广泛的经验。

微软花了一段时间才跟上这个程序,但现在他们已经有了EF,它将会长期存在并成为标准。如果你对ORM都没有经验,我建议开始学习EF,从长远来看它会更有利于你的职业发展。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接