是应该扩展还是封装ORM对象?

时间:2009-06-24 12:34:48

标签: c# .net oop orm llblgenpro

我无法理解如何使用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
}

公开像属性这样的实体字段感觉很尴尬,因为我正在重新映射。对于列表类型,它甚至更糟,因为我必须编写一个“添加”和“删除”方法以及一个公开List的属性。

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

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

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

3 个答案:

答案 0 :(得分:5)

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

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

<强>更新

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

直接来自MSDN

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

等同于

class Earth : Planet, IRotate, IRevolve { }

答案 1 :(得分:3)

Dunno如果在LLGLGen中是可能的,但是在使用ORM时我通常做的是创建一个持久化类的接口,在你的示例中是IBook。我通过包装类中的公共getter公开了这个接口。这样,如果需要,您可以根据需要向您扩展IBook,如果您需要在其字段中添加一些自定义行为。

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

  1. 您发布的方式。基本上,重新映射一切
  2. 我发布的方式,将ORM实体公开为接口
  3. 直接公开ORM实体
  4. 我不喜欢#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
        }
    }
    

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

答案 2 :(得分:1)

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

通过这个,我的意思是关系数据和对象的世界彼此非常非常不同。例如,在数据库中,所有数据都是公开的。在object-land中,数据被封装,并且非常明确地不公开。在database-land中,所有关系都是双向的,而在object-land中,集合中的对象可能没有对其父级的任何引用。在数据库领域,程序是全球性的。在object-land中,过程是对象的本地,其中包含代理数据。

由于这些原因以及更多,创建表示数据库表的对象的方法本身就很痛苦。虽然是,但从技术上讲它们是对象,它们具有数据库 - 土地的语义。如你所经历的那样,让它们生活在物体土地上,即使不是不可能也是困难的。这可以称为数据优先

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

我意识到这并没有具体回答你的问题,但我希望它提供了一些关于为什么你很难概念化你的实体的背景:它们首先是表,而实体是第二。