activerecord - C# .NET 中的数据映射器问题

标签 activerecord domain-driven-design data-access-layer repository-pattern datamapper

似乎有很多关于 DDD、存储库、数据映射器等的“噪音”......而很少有“真实世界”的实现代码来向像我这样的新手展示什么是“好”的,什么是“坏”。

我刚看完这本书Architecting Applications for the Enterprise ,这让人大开眼界。我目前在工作中的一个项目中使用 Active Record 模式,并且一直在努力将其更改为使用领域模型。我使用了书中的许多架构示例,以及本书随附的 Northwind 初学者工具包代码下载。

一切都很顺利,但现在我遇到了我的第一个“真实世界”数据映射器问题......也就是说,我的映射器不再只是负责获取一个实体对象并将其持久保存到数据库中,我现在有一个 Entity 对象,它有一个 IList<> 集合也需要映射。

主要的实体对象是Expert,这里是代码:

   public class Expert
   {    
          public int ID { get; set; }
          public string FirstName { get; set; }
          public string LastName { get; set; }
          public virtual IList<Case> Cases { get; protected set; }
   }

这里是 Expert 集合的实现,Case 对象:

   public class Case
   {
          public int ID { get; set; }
          public string Name { get; set; }
   }

再简单不过了。

现在,每个实体项都有一个 DataMapper,但我的问题是,当我在我的 ExpertDataMapper 代码中映射 Case 集合时,“正确”的方法是什么?

在书中,ExpertDataMapper 中嵌入了实际的 SQL 代码,它调用 ADO.NET 代码,获取 IList 集合中的所有 Case 项目,并为每个项目调用一次该代码。这是一些伪代码:

   public virtual void Create(Expert expert)
   {
          // Insert expert in the Expert table
          SqlHelper.ExecuteNonQuery(ProviderHelper.ConnectionString, CommandType.Text,
          "SQL TO INSERT EXPERT", this.BuildParamsFromEntity(expert));

          // Insert individual Case items in the Case table
          foreach (Case c in expert.Cases)
          {
                 SqlHelper.ExecuteNonQuery(ProviderHelper.ConnectionString, CommandType.Text,
                 "SQL TO INSERT CASE", this.BuildParamsFromEntity(order));
          }
    }

所以我立刻想到了两个错误:

  1. 为什么 ExpertDataMapper 有 SQL 调用(或存储过程调用)来插入嵌入在其自身中的 Case?对我来说,这打破了封装的想法……ExpertDataMapper 应该对案例如何插入数据库一无所知,这是 CaseDataMapper 的工作。

我假设 ExpertDatMapper 应该将插入案例的任务委托(delegate)给 CaseDataMapper...但我不知道一个 DataMapper 实例化另一个 DataMapper 是“正确”还是“错误”。这是否被视为 DataMappers 的常见问题?我找不到关于这个我认为相当普遍的问题的指导。

  1. 如果我有 100 个与该专家相关的案例,那么是否创建了 100 个案例对象,以及 100 个针对数据库的插入语句?我觉得这件事有些“不对劲”,而且违背了我更好的判断。现在,如果我足够幸运能够使用 SQL Server 2008,我可以使用表值参数,它不会那么糟糕,但我们现在是 2005 年。

所以,当谈到 DataMappers 时,我还没有看到任何简单创建、更新等的具体实现。在具有简单集合关联的实体对象的 DataMaper 上。我正在阅读的书没有支持它的代码(而且那里的代码对我来说看起来很可疑)。

我已经阅读并拥有 Martin Fowler 的 P of EAA 书,所以请不要让我看那里。我也知道我可以使用的 ORM 工具,因此我不需要亲自实现 DAL。我玩过 EF 4.0,我喜欢它。但是对于这个项目,我没有使用 ORM 工具的选项。

大多数书籍/示例似乎都停留在相同的基本前提下,即只要我的实体对象与它通过 DataMapper 持久化到的表之间存在一对一的关联,世界就是玫瑰色的使用域模型方法...

如果我所有的实体都是一对一的,我还不如只使用 Active Record 并完成它。

有任何指导、建议和见解吗?抱歉,这是一篇很长的帖子,但我在这里阅读了其他有类似问题的帖子,但没有关于如何处理此处提出的问题的真正具体的答案或建议。

最佳答案

关于第一点,从“SOLID”的角度来看,您是正确的。与其本身处理持久性,不如实现 ExpertDataMapper 使用的 CaseDataMapper 更易于维护且冗余更少。可能有几个原因没有这样做,主要是与简单性有关。如果你有一个单独的类应该在同一个事务中工作,你必须传递事务。这本身并不可怕,但它引入了更多关于如何使实现架构无关的问题。如果你只是四处传递事务,你就会耦合到普通的 ADO.NET,并且以后不能升级到像 MSEF、NHibernate、Linq2SQL 等的 ORM。所以你需要一个 UnitOfWork 模式,它允许你隐藏实际的事务或 session 或数据上下文或存储库中的任何内容。到目前为止,这个相对简单的代码片段现在是包含大量内容的两个完整类定义。

为了避免为了说明一个 Expert 而进行的所有这些,他们只是将用于保存 Cases 的代码放在 Expert 中。这基本上假设 Case 将始终被引用为 Expert 的子级,因此将功能分割成可以重用的东西是不值得的。吻。这个假设可能是正确的;如果不是,则留给读者作为练习将该逻辑重构为帮助程序。

关于第二点,你也说对了,确实没办法。从 SQL Server 还不知道的数据创建的每一行都必须一次插入一行。您可能会以某种方式设置批量插入,但我可以保证在您进入数千条记录之前,这比它的值(value)更麻烦。您使用的任何 ORM 都会产生相同的 SQL。

关于不使用 ORM:对于足够复杂的域/数据模型,如果您不使用预制 ORM,如果您希望它符合 SOLID 等设计方法,您最终将自己推出。

关于activerecord - C# .NET 中的数据映射器问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5006279/

相关文章:

mysql - Rails4 : How could ActiveRecord. 查找返回一个与查询对象具有不同 ID 的对象?

unit-testing - 不要模拟域对象规则?

domain-driven-design - 如何创建多语言领域模型

c# - 当来自 db 的数据不好时是否应该抛出异常?

domain-driven-design - 存储库添加和创建方法

ruby-on-rails - 单表继承 (STI) 父 ActiveRecord .subclasses .descendants 返回空

ruby-on-rails - ActiveRecord迁移中序列化列的默认值

java - DDD 中的聚合对象,这是潜在的聚合吗?

ios - 如何使用 Realm 编写更好的数据访问层

ruby-on-rails - 如何从Activerecord查询结果集中删除项目?