Следует ли расширять или инкапсулировать объекты ORM? - PullRequest
3 голосов
/ 24 июня 2009

У меня проблемы с пониманием того, как использовать объекты, сгенерированные 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. Это тоже нехорошо.

Мне интересно, не стоит ли мне просто расширить объект BookEntity и добавить туда свою бизнес-логику? Или, может быть, использовать частичные?

В примерах LLGLGen они напрямую используют объекты-сущности, но мне это кажется очень грязным. Я хочу иметь объекты, которые также могут иметь методы для моей бизнес-логики (например, CountPages) в коде выше.

Ответы [ 3 ]

5 голосов
/ 24 июня 2009

Я никогда не использовал LLBLGen для отображения, но большинство инструментов ORM, с которыми я работал, генерируют частичные классы. Затем я добавляю любой пользовательский код / ​​логику, которую я хотел бы добавить к этим объектам в отдельном файле (чтобы они не перезаписывались при повторной генерации частичных классов).

Кажется, работает довольно хорошо. Если вы не получите частичные классы из своего ORM, я бы создал Facade, который обернет ваш объект данных вашей бизнес-логикой ... таким образом, эти два разделятся, и вы можете перегенерировать один без перезаписи другого. *

UPDATE

Частичные классы поддерживают реализацию интерфейса в одном объявлении частичного класса, а не в другом. Если вы хотите реализовать интерфейс, вы можете реализовать его в своем частичном файле пользовательского кода.

Прямо от MSDN :

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

эквивалентен

class Earth : Planet, IRotate, IRevolve { }
3 голосов
/ 24 июня 2009

Не знаю, возможно ли это в LLGLGen, но что я обычно делаю при работе с ORM - это создание интерфейса с постоянным классом, в вашем примере IBook. Я выставляю этот интерфейс через публичный метод получения из класса-оболочки. Таким образом, если потребуется, вы можете расширить свой IBook так, как вам нужно, если вам нужно добавить какое-то пользовательское поведение в его поля.

Вообще говоря, я думаю, что есть 3 способа "привязки" ваших ORM-сущностей к вашему домену:

  1. То, как вы отправили. По сути, переназначить все снова
  2. Как я написал, выставьте ORM-сущность в качестве интерфейса
  3. Прямой доступ к ORM-сущности

Мне не нравится # 1, потому что мне не нравится иметь 2 сопоставления в моем приложении. СУХОЙ, ПОЦЕЛУЙ и ЯГНИ нарушаются этим.

Мне не нравится # 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
    }
}

Я не знаю, является ли это узнаваемый шаблон дизайна, но это то, что я использую, и до сих пор он работал довольно хорошо:)

1 голос
/ 23 августа 2009

Вы чувствуете боль несоответствия между различными парадигмами реляционных данных и объектов.

Под этим я подразумеваю, что миры реляционных данных и объектов очень сильно отличаются друг от друга. Например, в базе данных все данные общедоступны. В объекте земля данные инкапсулированы и, в частности, не обнародованы. В земле базы данных все отношения являются двунаправленными, тогда как в земле объектов объект в коллекции может не иметь никакой ссылки на своего родителя. В земле базы данных процедуры являются глобальными. В объекте-земле процедуры являются локальными для объекта, который содержит данные о действии.

По этим и другим причинам подход, который создает объекты, представляющие таблицы базы данных, неизбежно будет болезненным. Хотя да, технически они являются объектами, но у них есть семантика базы данных. Заставить их жить на объектной земле, как вы уже испытали, трудно, если не невозможно. Это может упоминаться как data-first .

Лучший подход (на мой взгляд) заключается в том, чтобы сосредоточиться на отображении объектов в базу данных, а не в сопоставлении базы данных с объектами. Это можно назвать object-first , и очень хорошо поддерживается NHibernate . Этот подход подчеркивает тот факт, что база данных - это деталь реализации системы, а не предписание проекта.

Я понимаю, что это не дает конкретного ответа на ваш вопрос, но я надеюсь, что он предоставит некоторый контекст для того, почему вам трудно осмыслить ваши сущности: они являются таблицами первыми, а сущностями вторыми.

...