代码之家  ›  专栏  ›  技术社区  ›  Robert Massa

应该扩展或封装ORM对象吗?

  •  3
  • Robert Massa  · 技术社区  · 15 年前

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

    暴露实体的字段(如属性)感觉很尴尬,因为我正在重新映射。对于列表类型,情况更糟,因为我必须编写一个“add”和“remove”方法以及一个公开列表的属性。

    在上面的例子中,在booktype setter中,我需要访问booktypeentity对象,我可以通过使用booktype对象的id oh实例化一个新的对象来获得这个对象。这也感觉不好。

    我想知道是否不应该扩展BookEntity对象并在其中添加业务逻辑?或者可能使用分词?

    在llglgen示例中,它们直接使用实体对象,但在我看来这非常混乱。我希望在上面的代码中有对象,这些对象还可以有用于我的业务逻辑的方法(如countpages)。

    3 回复  |  直到 15 年前
        1
  •  3
  •   cwap    15 年前

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

    一般来说,我认为有三种方法可以“映射”您的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
        }
    }
    

    我不知道这是否是一个可识别的设计模式,但这正是我所使用的,迄今为止它工作得相当好:)

        2
  •  5
  •   Justin Niessner    15 年前

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

    似乎工作得很好。如果您没有从ORM中获得部分类,我将创建一个外观,它用业务逻辑包装您的数据对象……这样,两个类就分离了,您可以在不覆盖另一个的情况下重新生成一个。

    更新

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

    直接从 MSDN :

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

    相当于

    class Earth : Planet, IRotate, IRevolve { }
    
        3
  •  1
  •   Bryan Watts    15 年前

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

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

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

    更好的方法(在我看来)是将对象映射到数据库,而不是将数据库映射到对象。这可以称为 对象优先 ,并得到了很好的支持 NHibernate . 这种方法强调这样一个事实:数据库是 实施详细信息 一个系统,而不是一个设计原则。

    我意识到这并不能明确回答你的问题,但我希望它能提供一些背景,解释为什么你很难概念化你的实体:它们首先是表,其次是实体。