Entity Framework - как фильтровать загруженные навигационные / реляционные свойства? - PullRequest
4 голосов
/ 23 июня 2011

У меня есть следующие сущности Entity Framework 4.1 и отношения

Концерт ConcertId, AdministratorUserId, Имя, IsDeleted

Бронирование BookingId, ConcertId, UserId, IsDeleted

UserId , UserId, Name, IsDeleted

Отношения Концерт 1 ..... M Бронирование 1 .... 1 Пользователь

Теперь я пытаюсь выбрать все Концерты для определенного AdminstratorUserId, но также включить все Заказы для каждого Концерта, а также информацию о Пользователе для каждого бронирования. Я также хотел бы применить фильтр, где IsDeleted == false для каждого концерта, бронирования и пользователя. Я хотел бы вернуть список концертов, чьи данные о бронировании и сведениях о пользователе были сохранены как навигационные свойства.

В SQL это то, чего я пытаюсь достичь:

SELECT *
FROM concert c, booking b, user u
WHERE c.ConcertId = b.ConcertId AND b.UserId = u.UserId AND c.AdministratorId = 10
AND c.IsDeleted = false AND b.IsDeleted = false AND u.IsDeleted = false

Насколько мне известно, использование метода "Включить" для ускорения загрузки не позволяет выполнять фильтрацию или подзапросы дочернего объекта, в который он загружается; он возвращает все записи для этого объединения, поэтому я попытался использовать анонимную проекцию следующим образом:

int adminId = 10;

var concerts = _context.Concerts
    .Where(p => p.AdministratorId == adminId && p.IsDeleted == false)
    .Select(p => new {
        Concerts = p,
        Bookings = p.Bookings
            .Where(q => q.IsDeleted == false && q.User.IsDeleted == false)
            .Select(r => new {
                Bookings = r,
                User = r.User
            })
            .AsEnumerable()
            .Select(q => q.Bookings)
    })
    .AsEnumerable()
    .Select(p => p.Concerts)
    .ToList();

Тем не менее, он по-прежнему возвращает все записи и не отфильтровывает те, где IsDeleted = true. У кого-нибудь есть идеи или предложения, как мне убрать этот чудовищный запрос?

Я также попробовал метод, подобный этому (http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx),, который снова не проходит (возвращает все заказы, даже удаленные):

var concertsQuery = (ObjectQuery<Concert>)_context.Concerts
    .Where(p => p.UserId == userId
        && p.IsDeleted == false
        && p.Bookings.Any(q => q.IsDeleted == false && q.User.IsDeleted == false)
    );

var concerts = concertsQuery.Include("Bookings").Include("Bookings.User").ToList();

Ответы [ 2 ]

2 голосов
/ 23 июня 2011

Избавьтесь от этих AsEnumerable, они превратят ваш запрос в linq-to-objects:

var concerts = _context.Concerts
    .Where(p => p.AdministratorId == adminId && p.IsDeleted == false)
    .Select(p => new {
        Concerts = p,
        Bookings = p.Bookings
            .Where(q => q.IsDeleted == false && q.User.IsDeleted == false)
            .Select(r => new {
                Bookings = r,
                User = r.User
            })
    })
    .ToList();
1 голос
/ 28 июня 2011

Итак, мне удалось решить эту проблему, выполнив запрос LINQ согласно ответу Ладислава, но затем используя анонимный объект, чтобы повторно присоединить отдельные объекты к их правильным реляционным свойствам, как показано ниже:

var concertResult = new List<Concert>();

foreach (var concertObject in concerts)
{
    var concert = concertObject.Concert;

    foreach (var bookingObject in concertObject.Bookings)
    {
        var booking = bookingObject.Booking;
        booking.User = bookingObject.User;
        concert.Bookings.Add(booking);
    }
    concertResult.Add(concert);
}
...