.NET ORM需要虚拟,而不能处理密封?

时间:2011-03-19 19:59:38

标签: .net nhibernate entity-framework orm class-design

我刚刚开始使用.NET ORM,我还没有在Entity Framework和NHibernate之间做出决定。但在这两种情况下,我遇到了一个问题,因为他们似乎希望我以各种方式破坏我的域模型的完整性,特别是在C#对象设计的更精细点上。这是关于这个问题的几个问题之一。


a reason virtual is not the default for methods in C#。我的域模型中的对象没有准备好对子类的行为做出承诺,除非在非常具体的情况下我将它们标记为这样。换句话说,对于我的域对象上很少的方法,为未指定的新功能添加一个钩子是合适的。

然而NHibernate希望我制作所有内容virtual,而Entity Framework希望我创建所有实体引用virtual。我意识到为什么他们需要它(创建代理对象),并且我意识到它实际上是继承的合法使用和virtual ---他们实际上 挂钩到我的属性以便添加新功能。但它让我觉得我必须使用完全与持久性相关的东西来注释我的域模型类,并且完全没有表达它们与实现者和消费者的实际合同。

作为一个较小的问题,我意识到我可能无法做任何事情,通常用sealed for all the usual reasons来表达我的课程。然而,这有点不那么光滑,因为为了持久性而从我的域对象中省略注释似乎不如添加注释那么糟糕。


令人沮丧的是,经过几年阅读像 Effective C#这样的书籍或像Eric Lippert那样的博客,它们提供了关于如何设计富有表现力和防弹的C#对象的建议,使用ORM的需求是让我把大部分知识扔出窗外。我希望这里有人可以指出我错在哪里,要么掌握他们的能力,要么是我对域建模和ORM角色的思考。

5 个答案:

答案 0 :(得分:19)

这不仅仅是.NET ORM - 同样的约束也适用于Java ORM。

尽管如此,在Java中,除非您明确声明,否则一切都是虚拟的,因此满足ORM的需求与您在sealed中找到的情况非常相似:

  

为了持久性而省略我的域对象中的注释似乎比添加注释更糟糕。

归结为:持久性无知是一个有价值的目标,但除非你愿意忽略像内存负载这样的小细节,否则它不是100%实现的目标。 em> performance 。

如果内存负载性能无关紧要,请停止使用代理并要求所有对象在水合后立即完全填充 - NHibernate可以做这通过配置。副作用是所有相关对象将一次加载,因此您最终将大部分数据库加载到内存中。该应用程序将需要大量内存并花费大量时间来启动 - 但它会起作用。

持久性是leaky abstraction - 虽然你可以将大部分内容隐藏在幕后,但总会有元素泄漏到应用程序的其他区域。

答案 1 :(得分:8)

如何轻轻地放这个......对不起,我不能。克服它。

我100%同意你,但使用框架总是意味着妥协。不想妥协?自己构建它。这就是它的全部内容。

为了减少对抗性,你的问题有一个解决方案,那就是使用像automapper这样的东西在你的漏洞持久性子系统和你的应用程序的其余部分之间进行转换。基本上,您可以保持域模型干净整洁,并按照您喜欢的方式进行设计,然后使用转换层在它与令人讨厌的丑陋ORM之间进行映射。

但是,这真的很多工作。对于少量的纯度,你放弃了你,省去了很多努力。

答案 2 :(得分:6)

  

有一个原因虚拟不是   C#中方法的默认值[link to   采访Anders Hejlsberg]。

Hejlsberg实际上是在讨论框架API设计。他没有谈论业务线应用程序。因此,他的规则在LOB应用程序中应用较少。由于您使用的是O / RM,因此您可能正在编写LOB应用程序。

  

通常表达我的注释   所有常见的密封课程   原因[链接到Eric Lippert的博客]。

您正在引用Eric Lippert的一篇文章,他在C#编译团队的工作环境中撰写了该文章。一般Framework Design Guidelines实际上包含相反的准则:

  

不要密封课程而没有   有充分理由这样做。 [第6.3段]

换句话说,Eric Lippert所说的是不是的共同规则。

就个人而言,当我编写LOB应用程序时,我实际上会密封我的类并尽可能地编写非虚方法。但是,这与在后续版本中引入重大更改的更改无关,因为这几乎只是一个框架设计问题。

不,我这样做是因为它让我更容易对我的代码做出假设。换句话说:它使我的代码更易于维护。

然而,当我需要这样做时,我完全没有开启类或虚拟化方法的问题。我这样做的主要原因是允许我的代码可以测试。

显然你也需要这种灵活性,既然你正在编写一个LOB应用程序,那就要务实并记住:

  

他们更像是指引

答案 3 :(得分:1)

我理解沮丧。一种可能性是使用面向方面编程(AOP)框架(如PostSharp)在编译时标记所有属性virtual。这方面的缺点是PS编织过程涉及的开销增加了整体编译时间。

只是为了好玩:我现在正在研究一个研究项目,这是一个基于AOP的初步ORM(暂称为Trinity)。它的目标是拥有延迟加载的全部容量,而无需引入代理(或virtual关键字)。最重要的是,它允许模型独立持久化(不涉及继承,POCO对象等),但提供与NHibernate相同的功能。

AOP仍处于研究水平,但这是一个有趣的项目。一旦论文准备就绪,我将尝试开源项目。

答案 4 :(得分:0)

没有必要在NHibernate中创建所有virtual。 如果您不使用“动态代理”,那么您不必将所有内容都虚拟化,并且可以使您的课程密封。

NHibernate默认使用动态代理。通过这样做,NHibernate创建了一个继承自您的类的类,这样就可以确保在检索实例时只填充该类的标识符。只有在您首次需要访问其中一个属性时才会加载该类的属性。

您可以通过在类映射中指定lazy=false来禁用动态代理功能:

<class name="MyEntity" table="SomeTable" lazy="false">
</class>