【问题标题】:How to fully charge a business entity in a service layer如何在服务层对业务实体进行全额收费
【发布时间】:2014-08-08 20:55:51
【问题描述】:

对于这个问题,我真的找不到令人满意的解决方案。我有一个 n 层应用程序:

  1. 用户界面
  2. 展示(域模型用作 DTO,然后展示 ViewModel)
  3. 业务层(领域模型)
  4. 存储库和 DAL(数据模型)

我的问题是我需要在我的业务层中使用完整的对象。但是我不知道加载它们的最佳方法是什么。这个问题可能看起来很愚蠢——也许确实如此——但我对此感到非常困惑。我有以下课程(只是为了说明):

public class Library
{
    int ID {get;set;}
    string Name {get;set;}
    Book[] Novels {get;set;}
    Book[] TextBooks {get;set;}
}

public class Book
{
    int ID {get;set;}
    Library[] SalePoints {get;set;}
    string Name {get;set;}
    string Type {get;set;}
}

我有两个服务方法 LoadLibrary 和 LoadBook,其目的是填充这些各自的模型。我的问题是看起来我会陷入无限循环。我现在就解释一下。

我已经预加载了来自存储库的域模型(存储库使用 valueinjecter(unflatenning)将数据模型转换为域模型,因此基本上简单的字段(int、string 等)填充在结果中域模型,但 IEnumerable 属性不是。这就是为什么我要在服务层中加载它们。(还有两个原因为什么我不能将它们完全加载到我的存储库中:1/我使用的是通用存储库和2/业务规则的加载逻辑中继)

假设我做了以下事情:

public class LibrairiesProcessor
{
    public Library LoadLibrairyObject(int ID)
    {
        Library l = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;

        l.Novels = UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="Novel");
        l.TextBooks= UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="TextBook");


        for(int i=0;i<l.Novels.Count();i++)
        {
            l.Novels[i] = BooksProcessor.LoadBookObject(l.Novels[i].ID);
        }
    }
}

public class BooksProcessor
{
    public Book LoadBookObject(int ID)
    {
        Book b = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;


        b.SalesPoint = // Get From repo using the Unit of work

        for(int i=0;i<b.SalesPoint.Count();i++)
        {
            b.SalesPoint[i] = LibrairyProcessor.LoadLibraryObject(b.SalesPoint[i].ID);
        }

    }
}

没有无限循环吗? LoadLibraryObject() 调用 LoadBookObject() ,反之亦然......尤其是当你看到这本书的 SalePoint 是触发 LoadBookObject() 的图书馆时......最多同样的工作被做了两次,但我真的怀疑它会永远不会结束。

所以我想知道我是否做对了。我的目标是拥有完全加载的对象,以避免在使用之前怀疑对象是否已足够加载,但我不确定我想出的解决方案。你通常是怎么做到的?

这看起来像一个微不足道的问题,但我真的不知道如何加载我的东西。我觉得我进退两难了:

1/ 使用UnitOfWork.Repository手动填充所有属性和子属性,但是我会有很多代码和很多冗余代码。

2/ 使用部分加载的域模型,其中一些属性设置为 null,但我真的不希望这样。而且由于我的域模型很复杂,因此我没有直接使用 EF 对象,因此我不能使用它们的延迟加载。

3/从一个 LoadObjectMethod 到另一个调用 Service LoadObject 方法,以避免代码冗余,但看起来我引入了很多递归性。

我应该如何处理?

谢谢!

【问题讨论】:

    标签: asp.net-mvc domain-driven-design repository-pattern n-tier-architecture ddd-repositories


    【解决方案1】:

    这可能最适合CodeReview,但这里有一些想法可能会为您节省一些麻烦:

    • 拥有一个完全独立的 DAL 对象模型可能是矫枉过正。大多数现代 ORM 会为您处理域对象/数据库映射,而不会用持久性东西污染您的域模型。见Should I create in BLL project class the same like poco class in DAL project and return it to UI project if I want to display data from database?

    • 拥有通用存储库似乎是无法加载子实体列表的糟糕借口。至于“加载逻辑依赖于业务规则”,如果你指的是小说/教科书的区别,有other, less class-intensive ways这样做。

    • “由于我的域模型很复杂,所以我不直接使用 EF 对象,所以我不能使用它们的延迟加载东西”:您的模型到底有多复杂?从技术上讲,为什么不能使用延迟加载?

    • 作为一种良好做法,聚合不应使用可导航属性而是使用(列表)id 来引用彼此。从存储库获取聚合根的对象如果想要加载关联的聚合根,将调用另一个存储库。

    • UnitOfWork.LibraryRepo 看起来很尴尬。 UoW 应该在方法中注入或直接实例化,而不是作为单例提供。存储库也是如此。如果您将 ILibraryRepo 传递给方法,事情将更加可测试和松散耦合。工作单元和存储库是两个独立的概念,它们以某种方式在实体框架中融合,但从外部看,它们不应该像那样紧密地联系在一起。

    【讨论】:

    • 非常感谢纪尧姆的帮助。实际上,在搜索后,我读到在 DAL 中使用域对象是不好的做法。我实际上不知道 EF 可以在不弄乱我的域对象的情况下完成这项工作。关于子实体的加载,如果我完全使用 EF 肯定会很容易。我实际上相信在我的情况下,单独的 DAL 对象模型是矫枉过正,它不会带来任何东西。当我说我的领域很复杂时,实际上我的意思是我的领域模型不是 POCO。也许我需要仔细看看 EF,我可能低估了它的功能。
    • 但是,如果 Aggregates 仅包含 ID 的根,则意味着我的模型中的属性必须如下所示: ICollection AggregateRoot2 {get;放;} ?但是这样做,看起来我正在失去 OOP 的便利性,不是吗?您是说我的域对象必须是 POCO,有时具有 ListofIDs 和简单聚合的可导航属性?谢谢,实际上我正在我的服务类中实例化 UOW!我将继续在 Code Review 中发布这个问题; )
    • 这并不完全是代码审查的主题。我们只审查工作代码。再次推荐 Code Review 之前请参考this post
    • @Joey 聚合是一个一致性边界,如果可以的话,你应该避免在同一个业务事务中修改多个聚合。因此,对从 AR 到 AR 的导航属性的需求应该是有限的。此外,通过 ID 引用 AR 可以提高存储可扩展性。您可以在 Vaughn Vernon's Effective Aggregate Design part 2(第 8 页)或 his book 中找到所有相关信息。
    • @Joey 我不认为 OOP 一定意味着加载一个完整的对象图,该图可以一次性从头到尾遍历。 OOP 是关于消息传递的。您可以完美地让编排器对象通过将消息传递到存储库来获取对小图的引用,然后在需要时从另一个存储库获取对相关图的另一个引用。此外,Law of Demeter 警告我们不要深入嵌套对象。如果您尝试深入到跨越诸如聚合之类的概念障碍,则 IMO 尤其如此。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2016-12-11
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 2013-11-27
    相关资源
    最近更新 更多