Используйте ValueInjecter, чтобы скопировать POCO EntityFramework в DTO, не вызывая ленивых списков загрузки и свойств

У меня проблема с использованием ValueInjecter для создания глубоких клонов POCO EntityFramework в аналогичные классы DTO.

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

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

Мой реальный проект довольно сложный, но в качестве примера я взял пример NerdDinner и повторил проблему гораздо проще. (NerdDinner - это пример кода для разработчиков с EF4 ( пример ScottGu NerdDinner).

Итак, у меня есть два модельных класса.

public class Dinner
{
    public int DinnerId { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }
    public virtual ICollection<RSVP> Rsvps { get; set; }
}

а также

public class RSVP
{
    public int RsvpID { get; set; }
    public int DinnerID { get; set; }
    public string AttendeeEmail { get; set; }
    public virtual Dinner Dinner { get; set; }
}

Я также создал класс DTO:

public class DinnerDTO
{ 
    public int DinnerId { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }
}

Обратите внимание, что у меня нет коллекции Rsvps, найденной в Dinner в моем DinnerDTO,

Также важно, что я использую соглашение CloneInjection для глубокого клонирования объекта. Этот код предлагается как здесь, на SO, так и на многих других сайтах в качестве подхода к выполнению глубокой инъекции клона. Этот код находится здесь: CloneInjection Code

Теперь, чтобы подчеркнуть ленивую загрузку, я пошел и вставил 10000 RSVP для Обеда с Id = 1.

Затем я выполняю следующий код:

var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);

Если я установлю точку останова на линии с InjectFrom и перешагните через него, есть значительное отставание, поскольку оно лениво загружает 10000 RSVP. Если я также установил точку останова в коде CloneInjection в обоих Match и SetValue методы, ни один из них не срабатывает до тех пор, пока не будет решена задержка загрузки. Это говорит мне о том, что это должно быть что-то внутреннее в ValueInjecter, которое несет ленивую нагрузку RSVPs имущество.

Теперь, если я изменю приведенный выше код к этому: (добавив Include на запрос)

var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).Include("RSVPs").FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);

Это изменение вызывает "нетерпеливую нагрузку" в списке RSVP, и, как и ожидалось, задержка находится в строке с запросом, а InjectFrom линия проходит без каких-либо задержек.

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

Я прочитал этот пост ( Копирование NHibernate POCO в DTO, не вызывая ленивую загрузку или энергичную загрузку) и связанный с ним код, его подход, похоже, использует некоторые методы NHibernate, чтобы определить, является ли свойство неинициализированным прокси-сервером, и каким-то образом удалить их. Я не смог найти ничего похожего в EF4.

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

Есть ли какие-то средства, с помощью которых я могу переопределить это поведение в ValueInjecter? Как-то отложить оценку значений свойств, пока не будет абсолютно уверен, что я хочу значение, например, в SetValue метод ConventionInjection? Тогда по крайней мере это не будет оценка свойств, которые мой DTO даже не хочет.

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

Есть ли какой-нибудь лучший подход через EF, который я должен использовать? Я не хочу загружать все в базу данных.

Я полностью выключен, и проблема не в ValueInjecter вообще?

* РЕДАКТИРОВАТЬ * Я нашел решение и ответил на этот вопрос, мне все еще любопытно, если я просто делаю это неправильно, или если есть еще лучший подход.

1 ответ

Решение

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

Сначала я полностью отключил Lazy Loading в dbContext. Что-то похожее на это в конструкторе dbContext.

this.Configuration.LazyLoadingEnabled = false;

Я на самом деле не использовал функцию отложенной загрузки EF, поэтому отключение не было большой потерей. Это просто означает, что если я хочу, чтобы связанные сущности были заполнены, я должен указать их в Include по моему запросу. Ничего страшного.

Другая вещь, которую я сделал, была переделка соглашения о глубокой инъекции клона, чтобы использовать SmartConventionInjection Код найден здесь SmartConventionInjection источник. Помимо того, что инъекция еще более быстрая, чем базовая, она также не затрагивает значения свойств до SetValue call, так что даже если бы у меня были некоторые ленивые свойства загрузки, они не были бы затронуты, если бы у DTO тоже не было этого свойства.

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