Рекомендации по сопоставлению объектов домена (ORM) с объектами передачи данных (DTO)

Текущая система, над которой я работаю, использует Castle Activerecord для обеспечения ORM (Object Relational Mapping) между объектами Domain и базой данных. Это все хорошо, и в большинстве случаев на самом деле работает хорошо!

Проблема возникает с поддержкой Castle Activerecords для асинхронного выполнения, а точнее, SessionScope, который управляет сеансом, к которому принадлежат объекты. Короче говоря, случается плохое!

Поэтому мы ищем способ легко преобразовать (мыслить автоматически) из объектов Домена (кто знает, что БД существует и заботится) в объект DTO (кто ничего не знает о БД и не заботится о сессиях, атрибутах отображения или обо всем ОРМ).

У кого-нибудь есть предложения по этому поводу. Для начала я ищу базовое сопоставление объекта "один к одному". Доменный объект Person будет сопоставлен с именем PersonDTO. Я не хочу делать это вручную, так как это пустая трата времени.

Очевидно, что на ум приходит размышление, но я надеюсь, что с некоторыми лучшими знаниями в области ИТ, распространяющимися вокруг этого сайта, будет предложено "круче".

О, я работаю в C#, объекты ORM, как было сказано ранее, сопоставлены с Castle ActiveRecord.


Пример кода:

По запросу @ ajmastrean я привел ссылку на пример, который я (плохо) высмеял вместе. В этом примере есть форма захвата, контроллерформы захвата, объекты домена, репозиторий activerecord и асинхронный помощник. Это немного большой (3 МБ), потому что я включил DLL ActiveRecored, необходимые для его запуска. Вам нужно будет создать базу данных с именем ActiveRecordAsync на вашем локальном компьютере или просто изменить файл.config.

Основные детали примера:

Форма захвата

Форма захвата имеет ссылку на контроллер

private CompanyCaptureController MyController { get; set; } 

При инициализации формы она вызывает MyController.Load() private void InitForm () { MyController = new CompanyCaptureController(this); MyController.Load(); } Это вернется к методу с именем LoadComplete()

public void LoadCompleted (Company loadCompany)
{
    _context.Post(delegate
    {
         CurrentItem = loadCompany;
         bindingSource.DataSource = CurrentItem;
         bindingSource.ResetCurrentItem();
         //TOTO: This line will thow the exception since the session scope used to fetch loadCompany is now gone.
         grdEmployees.DataSource = loadCompany.Employees;
         }, null);
    }
}

это то место, где происходят "плохие вещи", так как мы используем дочерний список Company, который установлен как Lazy load.

Контроллер

Контроллер имеет метод Load, который был вызван из формы, затем он вызывает помощник Asyc, чтобы асинхронно вызвать метод LoadCompany, а затем вернуться к методу LoadComplete формы Capture.

public void Load ()
{
    new AsyncListLoad<Company>().BeginLoad(LoadCompany, Form.LoadCompleted);
}

Метод LoadCompany() просто использует Репозиторий, чтобы найти известную компанию.

public Company LoadCompany()
{
    return ActiveRecordRepository<Company>.Find(Setup.company.Identifier);
}

Оставшаяся часть примера является довольно общей: в ней есть два класса доменов, которые наследуются от базового класса, установочный файл для вставки некоторых данных и хранилище для предоставления возможностей ActiveRecordMediator.

5 ответов

Я решил проблему, очень похожую на эту, где я скопировал данные из множества старых контрактов веб-служб в контракты данных WCF. Я создал несколько методов с такими сигнатурами:

public static T ChangeType<S, T>(this S source) where T : class, new()

В первый раз, когда этот метод (или любая другая перегрузка) выполняется для двух типов, он просматривает свойства каждого типа и решает, какие из них существуют, основываясь на имени и типе. Он берет это "пересечение членов" и использует класс DynamicMethod для эмуляции IL, чтобы скопировать исходный тип в целевой тип, а затем кэширует полученный делегат в поточном безопасном статическом словаре.

Как только делегат создан, он непристойно быстр, и я предоставил другие перегрузки для передачи делегату для копирования свойств, которые не соответствуют критериям пересечения:

public static T ChangeType<S, T>(this S source, Action<S, T> additionalOperations) where T : class, new()

... так что вы можете сделать это для своего примера Person to PersonDTO:

Person p = new Person( /* set whatever */);
PersonDTO = p.ChangeType<Person, PersonDTO>();

И любые свойства как Person, так и PersonDTO (опять же, имеющие одинаковые имя и тип) будут скопированы с помощью метода, генерируемого во время выполнения, и любые последующие вызовы не должны будут передаваться, но будут повторно использовать один и тот же код, сгенерированный для этих типов в этом порядок (т. е. копирование PersonDTO для Person также повлечет за собой попадание кода).

Слишком много кода для публикации, но если вам интересно, я приложу усилия, чтобы загрузить образец в SkyDrive и опубликовать ссылку здесь.

Ричард

Используйте ValueInjecter, с его помощью вы можете сопоставить что угодно, например:

  • объект <-> объект
  • object <-> Form / WebForm
  • DataReader -> объект

и у него есть интересные функции, такие как: сплющивание и отстегивание

загрузка содержит много образцов

Вы должны autopper, о котором я писал здесь:

http://januszstabik.blogspot.com/2010/04/automatically-map-your-heavyweight-orm.html

Пока свойства называются одинаковыми на обоих ваших объектах, автомаппер будет обрабатывать их.

На самом деле я сейчас совсем запутался. Потому что вы говорите: "Поэтому мы ищем способ легко преобразовать (мыслить автоматически) из объектов Домена (которые знают, что БД существует и заботится) в объект DTO (которые ничего не знают о БД и не заботятся о сессиях", сопоставление атрибутов или всего прочего ORM)."

  1. Доменные объекты знают и заботятся о БД? Не в этом ли смысл доменных объектов содержать ТОЛЬКО бизнес-логику и полностью не знать о БД и ORM?.... У вас должны быть эти объекты? Вам просто нужно ИСПРАВИТЬ их, если они содержат все эти вещи... вот почему я немного запутался, как появился DTO

  2. Не могли бы вы рассказать подробнее о том, с какими проблемами вы сталкиваетесь при отложенной загрузке?

Я приношу свои извинения за то, что на самом деле не привожу здесь подробности, но основной подход ОО должен был бы сделать DTO членом класса ActiveRecord и позволить ActiveRecord делегировать средства доступа и мутаторы DTO. Вы можете использовать инструменты генерации кода или рефакторинга для быстрого создания классов DTO из классов AcitveRecord.

Другие вопросы по тегам