.NET ORM、不可变值对象、结构、默认构造函数和只读属性
Posted
技术标签:
【中文标题】.NET ORM、不可变值对象、结构、默认构造函数和只读属性【英文标题】:.NET ORMs, immutable value objects, structs, default constructors, and readonly properties 【发布时间】:2011-03-19 20:12:21 【问题描述】:我刚刚开始使用 .NET ORM,甚至还没有在 Entity Framework 和 NHibernate 之间做出决定。但是在这两种情况下,我都遇到了一个问题,他们似乎希望我以各种方式损害我的域模型的完整性,尤其是在 C# 对象设计的更精细点上。这是有关该主题的几个问题之一。
我非常习惯于使用如下所示的模式对适当的属性强制执行不变性:
public class Foo
private readonly string bar;
public string Bar return this.bar;
public Foo(string bar)
this.bar = bar;
这似乎不受 NHibernate 或实体框架的支持。他们想要默认构造函数和public
setter;甚至private
setter(和默认构造函数)似乎也可以工作(有时?),因为 ORM 可以使用反射。
我想我可以通过使用private
setter 和private
默认构造函数来解决这些问题。至少公共 API 没有受到损害。只是我正在修改我所有的类的实现以添加未使用的private
构造函数,并且必须相信future-Domenic,他理解我的设置器上的private
真的意味着“除了在构造函数中之外不要给我打电话。 "持久层正在泄漏到我的域对象设计中。
这似乎也没有必要——为什么 ORM 不知道使用非默认构造函数?也许他们可以,但我只是没有找到正确的博客文章来解释如何。
最后,在某些情况下,我的不可变值对象实际上很适合(不可变的)值类型,即struct
s。我的猜测是这是可能的,因为在数据库中,我的结构的字段将显示在存储父实体的同一行中。你能确认/拒绝吗? This blog post 看起来很有希望,因为它给出了肯定的答案,但是代码的数量(实际上是特定于所讨论的值类型)令人震惊。
令人沮丧的是,在阅读了诸如 Effective C# 之类的书籍或 Eric Lippert 之类的博客(它们为如何设计富有表现力和防弹的 C# 对象提供了很好的建议)之后,使用 ORM 的必要性是令人沮丧的。让我把很多知识扔出窗外。我希望这里的人能指出我错在哪里,无论是在我对他们能力的掌握中,还是在我对领域建模和 ORM 角色的思考中。
【问题讨论】:
您至少有三个问题可以归结为“我不喜欢 ORM,因为它们损害了我的设计。” ORM 是解决特定问题的工具。如果你不喜欢它们,为什么要使用它们?使用 ADO.NET 编写您自己的数据访问层。在键入样板代码几个月后,您可能会开始欣赏 ORM 引入的权衡取舍。 (几年前我做过类似的事情;我不会回去了。) 是的,当我开始深入研究 ORM 时,这些都是相关的问题。但我更愿意将它们归类为“帮助我了解 ORM 中的妥协”问题。我致力于使用 ORM,因为正如您所说的 ADO.NET 中的数据访问是疯狂的酱汁。并不是我不喜欢它们,而是我仍在学习它们并试图获得帮助,让自己处于正确的心态。 例如,如果我在阅读 EF 与 NHibernate 的比较时会想到“这会在我的域模型中引起更少的妥协?”,那么与与了解通常需要哪些妥协,而是根据特性、成熟度、能力等比较两个框架。 【参考方案1】:正如 cmets 所指出的,当/如果您采用 ORM 时,您将不得不做出一些妥协。在我看来,要记住的是,生产力的收益远远超过了这些妥协的“成本”。
我确实想向您指出(因为您是 EF 新手),您可以通过customize the T4 templates 生成 EF 实体和上下文。我想如果你玩这个,你可以消除大部分你不喜欢的东西。
【讨论】:
【参考方案2】:在使用过许多 ORM 之后,我感到很痛苦,使用接口、抽象和不可变对象在大多数 ORM 中都无法正常工作。正是出于这个原因以及我在大约 8 年前自己编写的其他原因,我使用自己的 ORM 发现了其中一些问题并找到了解决这些问题的方法。我会看一下 Micro ORM,看看你能找到什么,那里有很多,有丰富的功能集,而且很多包包更少。
我添加了一些新功能来处理我的一些痛苦。就像返回接口集合或其他抽象类型以及不可变类型的能力一样。
string sql = "Select * from simpleEntities";
ISqlQuery query = _factory.CreateSqlQuery(sql, nameof(AbstractFactoryTests.ReadImmutableEntityItems));
IList<IImmutableEntity> items = loader.ObtainItemsImmutable<IImmutableEntity>(query);
或:
IList<ISqlQuery> queries = new List<ISqlQuery>();
// Note, no register types needed here because of returnType parameter
ISqlQuery s1 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=1", "LoadAbstract.ParallelLoadItems1", typeof(Employee));
queries.Add(s1);
ISqlQuery s2 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=2", "LoadAbstract.ParallelLoadItems2", typeof(Manager));
queries.Add(s2);
ISqlQuery s3 = _factory.CreateSqlQuery("Select * from Employee where EmployeeType=3", "LoadAbstract.ParallelLoadItems3", typeof(DistrictManager));
queries.Add(s3);
IList<IEmployee> data = loader.ParallelLoadAbstractItems<IEmployee>(_factory, queries);
【讨论】:
以上是关于.NET ORM、不可变值对象、结构、默认构造函数和只读属性的主要内容,如果未能解决你的问题,请参考以下文章