LINQ to Entities — поддержка замыканий (лямбда)?

Я работал над проблемой, с которой я столкнулся при использовании LINQ to Entities при использовании замыканий.

Судя по всему, L2E не поддерживает замыкания. Имея в виду:

var users = from user in UserRepository.FindAll()
            select new UserDTO
            {
               UserId = user.UserId,
               Tickets = from ticket in TicketRepository.FindAll()
                         where ticket.User == user
                         select new TicketDTO
                         {
                           TicketId = ticket.TicketId
                         }
            };

(ПРИМЕЧАНИЕ. В предложении «где» существует проблема. Мне не разрешено сравнивать объект с другим объектом, поскольку они не являются примитивными типами EF. Разрешены только такие вещи, как Int32, Guid и т. д.)

, недействителен, потому что я не могу сравнить «ticket.User» с «user»

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

На самом деле мой сценарий намного сложнее, чем этот, но это сценарий, который мне нужно решить сейчас.

Обходной путь, который я нашел в Интернете, использует подзапрос. Это работает, но для моего сценария это не очень эффективно.

Вопрос:

Кто-нибудь из вас знает, если:

  1. Entity Framework 4 будет поддерживать замыкания в LINQ to Entities?
  2. Есть ли лучшее решение этой проблемы, чем использование подзапросов?
  3. Любые дополнительные знания, которые у вас есть по этой теме, будут очень признательны!

person Community    schedule 01.02.2010    source источник


Ответы (2)


Это не проблема, напрямую связанная с замыканиями. Проблема (вероятно) в том, что вы смешиваете сущности Entity Framework и объекты передачи данных. Поставщик LINQ пытается преобразовать дерево выражений вашего запроса в операторы SQL и терпит неудачу, потому что он не может отделить объекты передачи данных от сущностей, а база данных, конечно же, не может работать с объектами передачи данных.

Я предлагаю сделать разделение намного чище - сначала получить данные из базы данных, используя LINQ to Entity и, возможно, анонимные типы, если это необходимо, затем переключиться на LINQ to Objects для создания объектов передачи данных из извлеченных данных, и все должно быть хорошо. Что-то вроде следующего. (Просто отметить - я (безопасно) предполагаю, что репозитории возвращают IQueryable<T>s (иначе все это вообще не должно работать).)

var result = UserRepository
      .FindAll()
      .Select(user => new
         {
            UserId    = user.UserId,
            TicketIds = TicketRepository
                           .FindAll()
                           .Where(ticket => ticket.User.UserId == user.UserId)
                           .Select(ticket => ticket.TicketId)
         });

Преобразование этого результата запроса в объекты передачи данных теперь выполняется прямолинейно. Обратите внимание, что пользователи сравниваются по идентификаторам, поскольку Entity Framework (пока еще) не поддерживает сравнение по ссылке.

person Daniel Brückner    schedule 01.02.2010
comment
Это неправильно. L2E вполне способен материализовать POCO, не являющиеся сущностью, даже в v1. См. blogs.teamb.com/craigstuntz/2009/12/31/38500 Если на то пошло, L2S тоже может это делать. - person Craig Stuntz; 02.02.2010
comment
Я не хотел сказать, что Entity Framework не может проецироваться на POCO, но что он не может обрабатывать POCO в произвольных позициях. Но глядя на это снова, вы правы. Пример должен работать, потому что используются только проекции на POCO. Таким образом, проблема, вероятно, заключается в эталонном сравнении пользовательских сущностей. - person Daniel Brückner; 02.02.2010
comment
Этот пример не будет работать, потому что TicketRepository .FindAll() вызовет исключение LINQ to Entities не распознает метод - person Tomas; 03.12.2013
comment
FindAll() не является методом Entity Framework, он был реализован автором вопроса. - person Daniel Brückner; 04.12.2013

Проблема здесь в том, что L2E не поддерживает ссылочное равенство материализованных объектов и объектов в БД, поэтому вам нужно сравнивать на основе PK:

var users = from user in UserRepository.FindAll()
            select new UserDTO
            {
               UserId = user.UserId,
               Tickets = from ticket in TicketRepository.FindAll()
                         where ticket.User.UserId == user.UserId
                         select new TicketDTO
                         {
                           TicketId = ticket.TicketId
                         }
            };

(Предполагая здесь, что ПК User называется UserId.)

person Craig Stuntz    schedule 01.02.2010