没有ORM的存储库模式

nab*_*rid 21 c# orm domain-driven-design repository-pattern

我在不使用ORM的.NET C#应用程序中使用存储库模式.但是,我遇到的问题是如何填充实体的一对多列表属性.例如,如果客户有一个订单列表,即如果Customer类有一个名为Orders的List属性,而我的存储库有一个名为GetCustomerById的方法,那么?

  • 我应该在GetCustomerById方法中加载Orders列表吗?
  • 如果订单本身有另一个列表属性等等怎么办?
  • 如果我想做懒加载怎么办?我会在哪里放置代码来加载客户的Orders属性?在Orders属性中获取{} accessor?但是,我必须将存储库注入域实体?我不认为这是正确的解决方案.

这也引发了诸如变更跟踪,删除等功能的问题?所以我认为最终结果是我可以在没有ORM的情况下进行DDD吗?

但是现在我只对我的域实体中的延迟加载List属性感兴趣?任何的想法?

纳比勒

我假设对于没有在域驱动设计中使用ORM的人来说这是一个非常常见的问题?任何的想法?

Dav*_*ers 12

没有ORM我可以做DDD吗?

是的,但ORM简化了事情.

说实话,我认为你的问题与你是否需要ORM无关 - 这是你对数据的过多考虑,而不是DDD成功的关键行为.就数据模型而言,大多数实体将以某种形式与大多数其他实体建立关联,从这个角度来看,您可以遍历整个模型.这就是您的客户和订单的样子,也许您认为需要延迟加载的原因.但是你需要使用聚合来将这些关系分解为行为组.

例如,为什么您将客户聚合建模为具有订单列表?如果答案是"因为客户可以订购",那么我不确定您是否处于DDD的心态.

有什么行为要求客户拥有订单清单?当您更多地考虑域的行为(即在什么点需要什么数据)时,您可以根据用例对事物进行建模,事情变得更加清晰和简单,因为您只需更改跟踪一小组对象在总边界.

我怀疑Customer应该是一个没有订单列表的单独聚合,Order应该是一个包含订单行列表的聚合.如果您需要对客户的每个订单执行操作,请使用orderRepository.GetOrdersForCustomer(customerID); 进行更改然后使用orderRespository.Save(order);

关于没有ORM的更改跟踪,您可以通过多种方式执行此操作,例如,订单聚合可能会引发订单存储库正在侦听已删除订单行的事件.然后可以在完成工作单元时删除这些内容.或者稍微不那么优雅的方法是维护已删除的列表,即您的存储库显然可以读取的order.DeletedOrderLines.

总结一下:

  • 我认为你需要更多地考虑行为而不是数据
  • ORM让变更跟踪的生活变得更轻松,但是你可以在没有变更跟踪的情况下完成任务,你绝对可以在没有变更跟踪的情

编辑回应评论:

我不认为我会为订单行实现延迟加载.您可以在订单上执行哪些操作而无需订单行?我怀疑的并不多.

但是,当它看起来没有意义时,我不是一个被限制在DDD的"规则"的人,所以...如果在不太可能的情况下,对订单对象执行了许多操作,那么"T需要的顺序线填充经常有大量的相关联的订单订单行(都必须是真实的我认为这是一个问题),那么我应该这样做:

在订单对象中有此私有字段:

private Func<Guid, IList<OrderLine>> _lazilyGetOrderLines;
Run Code Online (Sandbox Code Playgroud)

订单存储库将在创建时将其传递给订单:

Order order = new Order(this.GetOrderLines);
Run Code Online (Sandbox Code Playgroud)

这是OrderRepository上的私有方法:

private IList<OrderLine> GetOrderLines(Guid orderId)
{
    //DAL Code here

}
Run Code Online (Sandbox Code Playgroud)

然后在订单行属性可能看起来像:

public IEnumberable<OrderLine> OrderLines
{ 
    get 
    {
         if (_orderLines == null)
            _orderLines = _lazilyGetOrderLines(this.OrderId);

         return _orderLines;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑2

我发现这篇博文与我的解决方案类似,但更优雅:

http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance


Jef*_*nal 4

1) 我应该在 GetCustomerById 方法中加载订单列表吗?

将订单映射代码与客户映射代码分开可能是个好主意。如果您手动编写数据访问代码,从GetCustomerById方法调用该映射模块是您的最佳选择。

2)如果Order本身有另一个列表属性等等怎么办?

将所有这些放在一起的逻辑必须存在于某个地方;相关的聚合存储库是一个很好的地方。

3)如果我想延迟加载怎么办?我应该在哪里放置代码来加载客户中的 Orders 属性?在 Orders 属性内部 get{} 访问器?但随后我必须将存储库注入域实体中?我认为这不是正确的解决方案。

我见过的最好的解决方案是让您的存储库返回子类域实体(使用诸如Castle DynamicProxy之类的东西) - 这可以让您在域模型中保持持久性无知。