【问题标题】:Should one extend or encapsulate ORM objects?应该扩展或封装 ORM 对象吗?
【发布时间】:2009-06-24 12:34:47
【问题描述】:

我无法理解如何使用 ORM 生成的对象。我们使用 LLBLGen 将我们的数据库模型映射到对象。我们将这些对象封装在代表我们业务模型的另一层中(我认为)。

也许这段代码会更好地解释这一点。

public class Book { // The class as used in our application
    private BookEntity book;      // LLBLGen entity
    private BookType bookType;    // BookType is another class that wraps an entity

    public Book(int Id) {
        book = new BookEntity(Id);
    }

    public BookType BookType {
        get { return this.bookType; }
        set { 
            this.bookType = value; 
            this.book.BookType = new BookTypeEntity(value.ID);
            this.book.Save();
        }
    }

    public int CountPages() { }  // Example business method
}

像属性一样公开实体的字段感觉很尴尬,因为我要重新映射。使用列表类型更糟糕,因为我必须编写一个“添加”和“删除”方法以及一个公开列表的属性。

在上面的 BookType setter 示例中,我需要访问 BookTypeEntity 对象,我可以通过使用 BookType 对象的 ID 实例化一个新对象来获取此对象。这也感觉不好。

我想知道我是否不应该只扩展 BookEntity 对象并在那里添加我的业务逻辑?或者也许使用部分?

在 LLGLGen 示例中,他们直接使用实体对象,但这对我来说看起来很混乱。我想在上面的代码中拥有也可以为我的业务逻辑(如 CountPages)提供方法的对象。

【问题讨论】:

    标签: c# .net oop orm llblgenpro


    【解决方案1】:

    我从未使用 LLBLGen 进行映射,但我使用过的大多数 ORM 工具都会生成部分类。然后,我在单独的文件中添加任何我想添加到这些对象的自定义代码/逻辑(因此,如果重新生成部分类,它们不会被覆盖)。

    似乎工作得很好。如果你没有从你的 ORM 中获得部分类,我会创建一个门面,它用你的业务逻辑包装你的数据对象……这样两者就分开了,你可以重新生成一个而不覆盖另一个。

    更新

    分部类支持在分部类的一个声明中实现接口,而不是另一个。如果你想实现一个接口,你可以在你的自定义代码部分文件中实现它。

    直接来自MSDN

    partial class Earth : Planet, IRotate { }
    partial class Earth : IRevolve { }
    

    等价于

    class Earth : Planet, IRotate, IRevolve { }
    

    【讨论】:

    • LLBLGen 确实创建了部分类。但是当我的所有业务对象都是实体对象的一部分时,我将如何使用继承呢? (比如我想让我的书实现一个接口Document)
    • 更新了您的问题的答案。
    【解决方案2】:

    不知道在 LLGLGen 中是否可行,但我在使用 ORM 时通常会为持久类创建一个接口,在您的示例 IBook 中。我通过包装类的公共 getter 公开这个接口。这样,如果需要,如果您需要向其字段添加一些自定义行为,您可以按照您想要的方式扩展您的 IBook。

    一般来说,我认为有 3 种方法可以将您的 ORM 实体“映射”到您的域:

    1. 您发布的方式。基本上,重新映射所有内容
    2. 按照我发布的方式,将 ORM 实体公开为接口
    3. 直接公开 ORM 实体

    我不喜欢 #1,因为我不喜欢在我的应用程序中有 2 个映射。 DRY、KISS 和 YAGNI 都违反了这一点。

    我不喜欢 #3,因为它会使域层的任何消费者层直接与 ORM 层对话。

    .. 所以,我选择 #2,因为它似乎是 3 个邪恶中较小的一个;)

    [更新]小代码sn-p :)

    数据层中ORM生成的类:

    public class Book : IBook
    {
       public string ISBN {get; private set;}
    }
    

    在业务逻辑层中可以找到 IBook,以及一个图书包装器:

    public interface IBook
    {
        string ISBN {get;}
    }
    
    public class BookWrapper   //Or whatever you want to call it :)
    {
        //Create a new book in the constructor
        public BookWrapper()
        {
            BookData = new Data.MyORM.CreateNewBook();
        }
    
        //Expose the IBook, so we dont have to cascade the ISBN calls to it
        public IBook BookData {get; protected set;}
        //Also add whichever business logic operation we need here
        public Author LookUpAuther()
        {
           if (IBook == null)
              throw new SystemException("Noes, this bookwrapper doesn't have an IBook :(")
           //Contact some webservice to find the author of the book, based on the ISBN
        }
    }
    

    我不知道这是否是一个可识别的设计模式,但它是我使用的,并且到目前为止它工作得很好:)

    【讨论】:

    • 我不完全理解方法二。公开的接口会包含实体字段吗?这是我可以查找的模式,还是您可以提供一个非常小的代码示例?非常感谢!
    • 更新了一个小例子。我不知道 LLGLGen 是否允许您让 Book 从您的一个接口派生。
    • 噢 - 好吧.. 祝你好运:)
    • @Robert Massa:只要它可以生成部分类,就可以让它从你的接口派生。如果您将接口成员命名为与您完成的数据成员相同的名称!
    • LLBLGen 工具确实支持接口。编辑实体并单击代码生成选项 > 输出特定设置
    【解决方案3】:

    您正在感受到关系数据和对象的不同范式之间不匹配的痛苦。

    我的意思是,关系数据和对象的世界彼此非常非常不同。例如,在数据库领域,所有数据都是公开的。在对象领域,数据被封装并且非常明确地不公开。在数据库领域,所有关系都是双向的,而在对象领域,集合中的对象可能没有对其父对象的任何引用。在数据库领域,过程是全局的。在对象域中,过程对于包含作用数据的对象是本地的。

    由于这些以及更多原因,创建表示数据库表的对象的方法本质上是很痛苦的。虽然是的,但从技术上讲,它们是对象,但它们具有数据库领域的语义。正如你所经历的那样,让他们生活在客体中是很困难的,如果不是不可能的话。这可以称为数据优先

    更好的方法(在我看来)是专注于将对象映射到数据库,而不是将数据库映射到对象。这可以称为object-first,并得到NHibernate 的良好支持。这种方法强调数据库是系统的实现细节,而不是设计规则。

    我知道这并没有具体回答您的问题,但我希望它提供一些背景信息,说明您为什么难以概念化您的实体:它们首先是表,然后是实体。

    【讨论】:

    • 感谢您的回答布莱恩。在过去的几个月中,我已经完全意识到您在回答中所列出的内容。糟糕的是,我坚持使用具有数据优先方法的 LLBLGen(尽管下一个版本应该支持对象优先建模)。
    猜你喜欢
    • 2013-02-28
    • 1970-01-01
    • 2011-01-25
    • 1970-01-01
    • 2018-09-14
    • 2011-03-24
    • 2010-12-18
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多