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

发布于 2024-07-25 07:02:39 字数 1052 浏览 6 评论 0原文

我无法理解如何使用 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)的对象。

I'm having trouble understanding how to use ORM generated objects. We're using LLBLGen for mapping our database model to objects. These objects we encapsulate in another layer which represents our business model(I think).

Maybe this bit of code will explain this better.

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
}

Exposing the entity's fields like properties feels awkward, since I'm mapping all over again. With list-types it's even much worse, since I have to write a "Add" and "Remove" method plus a property that exposes List.

In the above example in the BookType setter I need access to the BookTypeEntity object, I can get this object by instantiating a new one using the ID oh the BookType object. This also doesn't feel good.

I'm wondering if I shouldn't just extend the BookEntity object and add my business logic there? Or maybe use partials?

In the LLGLGen examples they use the entity objects directly, but this looks very messy to me. I want to have objects which can also have methods for my business logic(like CountPages) in the code above.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

绮烟 2024-08-01 07:02:39

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

看起来效果很好。 如果您没有从 ORM 中获得部分类,我会创建一个外观,将您的数据对象与业务逻辑包装在一起……这样,两者就分开了,您可以重新生成一个而无需覆盖另一个。

更新

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

直接来自 MSDN

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

相当于

class Earth : Planet, IRotate, IRevolve { }

I've never used LLBLGen for mapping, but most of the ORM tools I've worked with generate partial classes. I then add any custom code/logic I'd like to add to those objects in a seperate file (so they don't get over-written if the partial classes are re-generated).

Seems to work pretty well. If you don't get partial classes from your ORM, I'd create a Facade which wraps your Data Object with your Business Logic...that way the two are seperated and you can re-gen one without overwriting the other.

UPDATE

Partial classes support implementing an Interface in one declaration of a partial class and not the other. If you want to implement an interface, you can implement it in your custom code partial file.

Straight from MSDN:

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

is equivilant to

class Earth : Planet, IRotate, IRevolve { }
孤芳又自赏 2024-08-01 07:02:39

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

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

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

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

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

.. 所以,我选择#2,因为它似乎是三害相权取其轻;)

[更新] 小代码片段:)

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

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

Dunno if it's possible in LLGLGen, but what I generally do when working with ORMs is to create an interface to the persisted class, in your example IBook. I expose this interface via a public getter from the wrapping class. This way, if needs will be, you can extend you IBook the way you want if you need to add some custom behaviour to its fields.

Generally speaking, I think there's 3 ways of "mapping" your ORM-entities to your domain:

  1. The way you've posted. Basically, remap everything again
  2. The way I posted, expose the ORM-entity as an interface
  3. Expose the ORM-entity directly

I don't like #1, cause I don't like to have 2 mappings in my application. DRY, KISS and YAGNI are all violated by this.

I don't like #3 cause it would make whatever consumer-layer of your domain-layer talk directly to the ORM layer.

.. So, I go with #2, as it seems to be the lesser of 3 evils ;)

[Update] Small code snippet :)

ORM-generated class in the data-layer:

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

IBook is found in the business-logic layer, along with a book wrapper:

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
    }
}

I don't know if this is a recognizable design-pattern, but it's what I use, and so far it has worked quite well :)

时光倒影 2024-08-01 07:02:39

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

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

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

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

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

You are feeling the pain of the mismatch between the different paradigms of relational data and objects.

By this, I mean that the worlds of relational data and objects are very, very different from each other. For example, in database-land all data is public. In object-land, data is encapsulated and very specifically not made public. In database-land, all relationships are bi-directional, whereas in object-land an object in a collection might not have any reference to its parent. In database-land, procedures are global. In object-land, procedures are local to the object which contains the acted-upon data.

For these reasons and more, an approach which creates objects that represent database tables is inherently going to be painful. While yes, technically they are objects, they have the semantics of database-land. Making them live in object-land, as you have experienced, is difficult if not impossible. This can be referred to as data-first.

A better approach (in my opinion) is to focus on mapping objects to the database, rather than mapping the database to objects. This can be referred to as object-first, and is supported very well by NHibernate. This approach emphasizes the fact that a database is an implementation detail of a system, not a design precept.

I realize this doesn't specifically answer your question, but I hope it provides some context as to why you are having a hard time conceptualizing your entities: they are tables first and entities second.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文