3

我无法理解如何使用 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 设置器示例中,我需要访问 BookTypeEntity 对象,我可以通过使用 BookType 对象的 ID 实例化一个新对象来获取该对象。这也感觉不好。

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

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

4

3 回答 3

5

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

似乎工作得很好。如果您没有从 ORM 中获取部分类,我会创建一个外观,它将您的数据对象与您的业务逻辑包装在一起......这样两者就被分开了,您可以重新生成一个而不覆盖另一个。

更新

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

直接来自MSDN

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

相当于

class Earth : Planet, IRotate, IRevolve { }
于 2009-06-24T12:43:47.170 回答
3

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

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

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

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

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

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

[更新] 小代码片段 :)

数据层中的 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
    }
}

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

于 2009-06-24T12:42:33.397 回答
1

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

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

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

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

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

于 2009-08-23T17:42:04.467 回答