Копирование NHibernate POCO в DTO без ленивой или активной загрузки

Мне нужно создать DTO из объектов NHibernate POCO. Проблема заключается в том, что объекты POCO содержат динамические прокси, которые не следует копировать в DTO. Я хочу загрузить все коллекции и ссылки, которые мне нужно перенести заранее, я не хочу, чтобы NHibernate начинал загрузку ссылочных коллекций, которые я не загружал заранее.

Несколько похожих вопросов по SO получили ответы, которые либо:

  1. Предложить Session.GetSessionImplementation().PersistenceContext.Unproxy();
  2. Предложить отключение Lazy Loading.

В моем случае первое предложение не имеет значения, так как, насколько я понимаю, оно вызывает стремительную загрузку, чтобы заменить прокси. На самом деле, он даже не работает - он не удаляет прокси в моих объектах. (Любое объяснение почему?)

Второе предложение - отключение отложенной загрузки, похоже, приводит к активной загрузке всех ссылок и коллекций, в основном к загрузке всей БД. Я ожидал, что если отложенная загрузка отключена и я не запросил коллекцию, она не будет загружена. (Я прав, что NHibernate не предлагает такой вариант?)

Я использую NHibernate 3.3.1 с быстрой конфигурацией.

Чтобы повторить мой основной вопрос, мне нужно создать DTO, чистые от прокси, скопированные из POCO, которые содержат прокси, и я не хочу загружать данные за этими прокси.

Любое полезное предложение, которое включает пример кода и автоматизирует процесс с помощью ValueInjecter / AutoMapper, будет чрезвычайно полезным.

Редактировать № 1:

Следуя предложению Роджера Алсинга использовать проекции, я понял, что на самом деле я ищу отображение, основанное на ValueInjecter. Вот почему. Первоначально мои DTO будут определены так же, как POCO модели. Это связано с большой кодовой базой, которая зависит от существующих POCO, передаваемых в клиентском проекте.

Используя проекции, мне нужно будет указать, какое подмножество полей должно быть скопировано, и это подмножество может отличаться в каждом контексте (так как в идеале DTO будет отличаться). Это будет означать много нового кода, представленного на стороне сервера, когда должен быть второй вариант.

Используя ValueInjecter, я смогу заполнить DTO по соглашению за один вызов, без написания конкретных прогнозов или необходимости поддерживать их в будущем. То есть, если я могу иметь ValueInjecter игнорировать объекты Proxy.

Учитывая, что использование проекций является хорошим, но не идеальным решением в моей ситуации, есть ли способ настроить что-то вроде ValueInjecter для копирования POCO без копирования прокси-серверов или безошибочной / отложенной загрузки при копировании?

4 ответа

Решение

Для решения ValueInjecter я рекомендую использовать SmartConventionInjection (необходимо скопировать код со связанной страницы в ваше решение)

и после укажите соглашение, которое не будет касаться свойств прокси

вот начало:

public class MapPoco: SmartConventionInjection
{
     protected override bool Match(SmartConventionInfo c)
     {
         return c.SourceProp.Name == c.TargetProp.Name;
     }
}

Я решаю это, выбирая DTO в качестве проекций, используя Linq или любой другой язык запросов, который может иметь O/R Mapper.

например

return from c in customers 
       select new CustomerDTO 
       {
          Name = c.Name , 
          Orders = c.Orders.Select (o => new OrderDTO {...} ) 
       };

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

Я использую следующий ValueResolver с AutoMapper:

/// <summary>
/// ValueResolver that will set NHibernate proxy objects to null, instead of triggering a lazy load of the object
/// </summary>
public class IgnoreNHibernateProxyValueResolver : IValueResolver
{
    public ResolutionResult Resolve(ResolutionResult source)
    {
        var prop = source.Type.GetProperty(source.Context.MemberName).GetValue(source.Value, null);
        var proxy = prop as INHibernateProxy;
        if (proxy != null && proxy.HibernateLazyInitializer.IsUninitialized)
        {
            return source.Ignore();
        }

        return source.New(prop);
    }
}

Посмотрите на проекции в Введение в QueryOver в NH 3.0

CatSummary summaryDto = null;
IList<CatSummary> catReport =
    session.QueryOver<Cat>()
        .SelectList(list => list
            .SelectGroup(c => c.Name).WithAlias(() => summaryDto.Name)
            .SelectAvg(c => c.Age).WithAlias(() => summaryDto.AverageAge))
        .TransformUsing(Transformers.AliasToBean<CatSummary>())
        .List<CatSummary>();
Другие вопросы по тегам