C#.NET中的Data Mapper问题

时间:2011-02-15 16:19:38

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

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

我刚刚读完了这本书Architecting Applications for the Enterprise,这让人大开眼界。我目前在工作中使用Active Record模式,并且一直在努力将其更改为使用Domain Model。我在本书中使用了很多架构示例,以及与本书相关的Northwind Starter Kit代码下载。

一切都很顺利,但现在我已经遇到了我的第一个“真实世界”数据映射器问题...而不是我的映射器只负责获取一个Entity对象并将其持久化到数据库,我现在拥有一个具有IList<>的Entity对象还需要映射的集合。

主要的Entity对象是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对象集合的实现:

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

不能比那简单得多。

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

在本书中,实际的SQL代码嵌入在ExpertDataMapper中,该代码调用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应该对Case如何插入数据库一无所知,这就是CaseDataMapper的工作。
  2. 我认为ExpertDatMapper应该将Case插入到CaseDataMapper中的任务...但我不知道一个DataMapper是否“正确”或“错误”来实例化另一个。这被认为是DataMappers的常见问题吗?我没有找到关于这个问题的指导,我认为这是很常见的。

    1. 如果我有100个与此专家关联的案例,那么创建了100个Case对象,并且针对数据库插入了100个语句?关于这一点对我来说感觉“错误”,违背了我更好的判断。现在,如果我足够幸运能够使用SQL Server 2008,我可以使用表值参数,它不会那么糟糕,但我们现在正处于2005年。
    2. 因此,当涉及到DataMappers时,我还没有在实体对象的DataMaper上看到任何具有简单集合关联的简单创建,更新等的具体实现。我正在阅读的这本书没有代码可以支持它(那里的代码看起来很可疑)。

      我已阅读并拥有Martin Fowler的EA EA书籍,所以请不要告诉我看那里。我也知道可以使用的ORM工具,所以我不需要自己实现DAL。我玩过EF 4.0,我喜欢它。但是对于这个项目,我没有选择使用ORM工具。

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

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

      任何指导,建议,见解?对不起,这是一个很长的帖子,但我在这里看到了其他类似问题的帖子,没有真正具体的答案或如何处理这里提出的问题的建议。

2 个答案:

答案 0 :(得分:4)

关于第一点,从“SOLID”的角度来看,你是对的。而不是处理持久性本身,实现ExpertDataMapper使用的CaseDataMapper将更易于维护和更少冗余。这可能不是出于几个原因,主要与简单性有关。如果您有一个应在同一事务中工作的单独类,则必须传递该事务。这本身并不可怕,但它引入了更多关于如何使实现架构独立的问题。如果您只是通过事务处理,那么您将与vanilla ADO.NET结合,并且无法在以后升级到像MSEF,NHibernate,Linq2SQL等ORM。因此,您需要一个UnitOfWork模式,它允许您隐藏实际事务或会话或DataContext或存储库中的任何内容。到目前为止,这个相对简单的代码片段现在是两个完整的类定义,有很多东西。

为了避免所有这些,为了说明一个Expert,他们只是将代码保存在Expert中。这基本上假设Case总是被引用为Expert的子代,因此将功能切换为可以重用的东西是不值得的。吻。这个假设可能是真的;如果没有,那么将它作为练习留给读者将该逻辑重构为帮助者。

关于第二点,你也是对的,而且真的没有办法解决它。从SQL Server无法了解的数据创建的每一行必须一次插入一行。你可能会以某种方式设置批量插入,但我可以保证比你的价值更麻烦,直到你进入成千上万的记录。您使用的任何ORM都会产生相同的SQL。

在不使用ORM的情况下:如果您没有使用预制ORM,那么如果您不想使用预制ORM,那么如果您希望它符合SOLID等设计方法,您将最终自行滚动。

答案 1 :(得分:3)

这实际上取决于个人偏好和意见。一些开发人员会说你所展示的代码没有任何问题。其他人会说每个班级只对自己的州和财产负责。其他人也会说其他的东西。你应该使用适合自己的东西,即:你觉得自己感觉舒适。就个人而言,我更喜欢使用EF Code First,它允许我轻松创建我的模型类和我的数据库上下文类。然后我简单地使用存储库模式和负责提交事务的UnitOfWork类来抽象。此外,依赖注入模式是松散您的类的好方法。我的类紧密耦合存在重大问题,如果不使用IoC容器我无法解决。

This article是迄今为止我见过的最佳资源。作者在创建一个完全可行且最重要的可扩展框架方面做得非常出色。

祝你好运:)