【问题标题】:Is injecting DAO into entities a bad thing?将 DAO 注入实体是一件坏事吗?
【发布时间】:2011-07-13 01:30:56
【问题描述】:

因此,像大多数新的 .NET 开发人员一样,您开始在各处传递 DataSet,尽管事情做得很好,但似乎并不正确。

下一个进展通常是创建扩展 DAL 基类的实体对象,以便您拥有即

public class User : UserDAL
{
    //User specific methods
}
public class UserDAL
{
  //Bunch of user specific properties
  public User Load(int Id)
  {
    //Some low level ADO calls
  }
  private User LoadFromDataSet(DataSet ds)
  {
     //Load entity properties from DataSet 
  }    
}

User 使用 ADO.NET 扩展具有低级数据访问调用的 UserDAL 对象。

从这里您将继续了解此实现意味着您与数据访问层绑定,并且您使用单独的实体、数据访问对象和 DAO 接口进行模拟或在需要时轻松更换 DAO。即

public UserDAO : IUserDAO
{
  //Data access Functions
}

通过使用泛型和反射或良好的 ORM,您可以减轻一些更常见的数据访问 CRUD 操作,即

Public UserDAO<User> : BaseDAO<User>, IUserDAO
{
  //BaseDAO deals with basic crud so more custom data access methods can go here 
}

所以基本上这就是我目前所处的位置,除了使用 IoC 来解决我想要的特定 IUserDAO 等其他一些不错的做法。但是,虽然我看到了这种结构的优势,但我也觉得我错过了旧的 User.Load(1) 方法调用。

我想知道的是,将我的 IUserDAO 注入到 User 实体中并让它处理基本的 CRUD 操作会是一件坏事吗?

我的理解是,作为 POCO,用户实体通过网络传递不会有任何问题,并且添加 Save()、Load() 等方法在数据传输对象的意义上没有相关性。

但话虽如此,我的实体通常具有延迟加载的集合,这在 DTO 意义上没有任何意义。此外,我相信使用 WFP 可以挑选我想要序列化的属性,或者至少我可以在需要通过网络发送它时创建一个新的 UserDTO。

所以基本上,除了那个问题之外,让我的用户实体包含 DataAccess 相关方法的其他问题是什么?也有人可以澄清一下我所说的是否被称为活动记录模式还是其他什么?

编辑:

克里斯蒂安利巴多指出:

至于潜在的缺点,与持久性代码、跟踪/更新关联时的重新定位、可测试性和查询有更大的耦合。

会有更大程度的耦合,但我的想法是这样的:

public class User
{
   IUserDAO userDAO;
   public User()
   {
         userDAO = IoCContainer.Resolve<IUserDAO>;
   }
  public User(IUserDAO userDAO)
   {
         this.userDAO = userDAO;
   }
   //DAL methods
}

所以耦合应该是最小的,至于可测试性,我不认为这是一个问题,因为我可以将一个模拟 DAO 注入到实体中。

感谢 Brian Hasden,这些资源非常好,但我想我只是想为我显然要做的事情找理由。感谢您提供上述理由。

【问题讨论】:

    标签: .net architecture


    【解决方案1】:

    我得出了同样的结论。加载在实体中通常没有意义,因为一旦有了实例,您要么创建一个新实体,要么已经加载了一个实体。多年来,我一直在使用带有保存(创建和更新)和删除的实体,没有任何问题。话虽如此,仍然有一个 DAO 来做其他事情通常很有用,所以你并没有完全结合 DAO 和实体。大多数情况下,对于我的实体,Save 和 Delete 方法只是调用 DAO 的 Save 和 Delete 方法。

    顺便说一句,我通常将脏标志与实体结合起来,以了解何时更改了属性,这样您就不会在实体未更改时进行不必要的保存调用。

    通常,除了包含私有成员的 getter 和 setter 之外什么都不做的实体意味着您正在使用一个贫乏的域模型。这是一个颇有争议的反模式。

    您可以在以下位置找到更多信息:

    http://www.martinfowler.com/bliki/AnemicDomainModel.html http://wrschneider.blogspot.com/2005/01/avoiding-anemic-domain-models-with.html http://www.dotnetjunkies.com/WebLog/richardslade/archive/2007/03/07/209401.aspx

    【讨论】:

    • 我明白你的观点是实例化一个新用户只是为了加载另一个用户,但是静态方法调用,即 User.Get(id) 呢?此外,我倾向于使用 NHibernate 作为我的 ORM,这有助于解决脏标志检查等问题。
    • 这就是我通常保留 DAO 或使用类似工厂模式的东西来创建和加载实体的地方。如果你在同一个库(不同的命名空间)中有 DAO 和实体,你可以将 load 方法设置为内部,这样你就不会有泄漏的抽象。
    • 意味着你可以做一些像 UserDAO.Get(id) 这样的事情,它会抓取数据集,然后将它传递给实体的内部加载方法,然后返回实体。当然,只需在实体本身中放置一个静态方法就可以工作并完成基本相同的事情。
    • 我永远不会在同一个程序集中拥有实体和 DAO,因为它具有隐含的依赖关系。至于假设 UserDAO 返回一个 DataSet,我真的不明白你为什么要这样做。您使用更高级别的 UserDAO 类返回数据集?为什么不返回一个用户对象?
    • 对不起,我一定没有说清楚。我并不是说 DAO 会返回一个数据集。当然,它会返回一个实体。我是说在 DAO 或实体中有一个静态 get 方法可以完成同样的事情。 DAO 加载实体或加载自身(数据记录、数据集...
    【解决方案2】:

    是的,您所描述的内容听起来就像活动记录(将数据库行制成一个对象,其逻辑可以将自身从/向数据库持久化)。毫无疑问,这是一种有效的技术。

    至于潜在的缺点,与持久性代码、跟踪/更新关联时的重新定位、可测试性和查询有更大的耦合。

    【讨论】:

      【解决方案3】:

      将持久性排除在域类的继承模型之外,很大程度上是为了编写可理解和可维护的代码。

      持久性与班级的真正职责是正交的。我注意到,inherit-from-DAO 方法将世界任意划分为两类,从职责和行为的角度来看,它们没有什么不同。一个班级属于哪一方往往会随着时间而改变。您不希望该更改影响该类的 API 以及与其他类的交互。

      示例:假设今天,您的 Product 类使用通过 IoC 检索的 IPriceCalculator。明天,你决定你需要允许修改每个产品的算法,所以你的 IPriceCalculator 变成了数据驱动的。这可能是一个痛苦的变化,特别是如果您的价格计算器已经有一个提供重要功能的基类。

      这个例子突出了另一个注意事项:从 DAO 类继承意味着您的 User 类刚刚失去了继承可能具有有用行为的 Person 类的能力。 (在 .NET 世界中,MarshalByRefObject 是人们需要继承的公共基类,它可能与基于继承的 DAO 要求相冲突。)对于单继承,为了获得可能需要单独考虑的功能而进行继承并不理想。

      【讨论】:

      • 您在讨论从 DAO 类继承,而我在讨论注入 DAO 并实质上将方法包装在实体中。
      • 我也真的不明白你的例子。我不明白为什么 IPriceCalculator 会成为 DAO,如果算法现在是基于每个产品的并且信息基于数据库中的数据,那么 IPriceCalculator 可能会使用诸如 IProductDAO 之类的 DAO,但不会成为一个 DAO。除非我错过了什么。
      猜你喜欢
      • 1970-01-01
      • 2017-01-27
      • 2014-09-02
      • 1970-01-01
      • 1970-01-01
      • 2012-03-21
      • 1970-01-01
      • 2010-11-15
      • 2011-04-03
      相关资源
      最近更新 更多