Entity Framework Включить с условием - PullRequest
4 голосов
/ 15 июля 2011

Мне нужно отфильтровать дилера по идентификатору и незавершенным чекам

Изначально он возвращал дилера только на основе идентификатора:

    // TODO: limit checkins to those that are not complete
    return this.ObjectContext.Dealers
        .Include("Groups")
        .Include("Groups.Items")
        .Include("Groups.Items.Observations")
        .Include("Groups.Items.Recommendations")
        .Include("Checkins")
        .Include("Checkins.Inspections")
        .Include("Checkins.Inspections.InspectionItems")
        .Where(d => d.DealerId == id)
        .FirstOrDefault();

Как видите, требование ограничить количество проверок. Вот что я сделал:

var query = from d in this.ObjectContext.Dealers
                            .Include("Groups")
                            .Include("Groups.Items")
                            .Include("Groups.Items.Observations")
                            .Include("Groups.Items.Recommendations")
                            .Include("Checkins.Inspections")
                            .Include("Checkins.Inspections.InspectionItems")
                            .Where(d => d.DealerId == id)
                        select new
                        {
                            Dealer = d,
                            Groups = from g in d.Groups
                                     select new
                                     {
                                         Items = from i in g.Items
                                                 select new
                                                 {
                                                     Group = i.Group,
                                                     Observations = i.Observations,
                                                     Recommendations = i.Recommendations
                                                 }
                                     },
                            Checkins = from c in d.Checkins
                                       where c.Complete == true
                                       select new
                                       {
                                           Inspections = from i in c.Inspections
                                                         select new
                                                         {
                                                             InspectionItems = i.InspectionItems
                                                         }
                                       }

                        };

            var dealer = query.ToArray().Select(o => o.Dealer).First();

            return dealer;

Это работает. Однако я не уверен, что поступаю правильно.

Какой лучший способ добиться того, что я сделал? Хранимая процедура может быть?

Я не уверен, что мне даже нужно больше использовать предложение Include

Спасибо.

Ответы [ 4 ]

3 голосов
/ 15 июля 2011

Если вы хотите загрузить отфильтрованные отношения одним запросом, вам действительно нужно выполнить такую ​​проекцию, но вам не нужны эти вызовы для Include.Как только вы строите проекции, включения не используются - вы вернули данные под свой контроль.

Хранимая процедура поможет вам, только если вы вернетесь к обычному ADO.NET, потому что хранимые процедуры, выполняемые через платформу Entity, не могутзаполнить связанные сущности (только сплющенные структуры).

Автоматическое исправление, упомянутое @Andreas, требует нескольких запросов к базе данных, и, насколько я знаю, это работает, только если отложенная загрузка отключена, потому что прокси-объект как-то не имеет информации о исправлении, и этовсе еще имеет свои внутренние флаги для каждого отношения как не загруженные, поэтому, когда вы обращаетесь к ним в первый раз, они все еще выполняют дополнительный запрос.

2 голосов
/ 15 июля 2011

Может быть, вы можете использовать механизм исправления отношений в EF ObjectContexts.Когда вы делаете несколько запросов в одном и том же контексте для сущностей, которые связаны ассоциациями, они разрешаются.Предполагая, что ваша связь между Дилерами и Checkins составляет 1: n со свойствами навигации на каждой стороне, вы можете сделать следующее:

var dealer = yourContext.Dealers
             .Where(p => p.DealerId == id)
             .FirstOrDefault();
if(dealer != null)
{
    yourContext.Checkins
           .Where(c => c.Complete && c.DealerId == dealer.DealerId)
           .ToList();

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

0 голосов
/ 04 июля 2013

Ваше принятое решение будет генерировать несколько запросов к базе данных.Как сказал Ладислав Мрнка, проекция - это единственный способ получить ваш результат одним запросом.Обслуживание вашего кода действительно сложно.Возможно, вы могли бы использовать IQueryable-Extension, который динамически создает проекцию и поддерживает ваш код в чистоте:

var query = this.ObjectContext.Dealers.SelectIncluding( new List<Expression<Func<T,object>>>>(){

    x => x.Groups,
    x => x.Groups.Select(y => y.Items),
    x => x.Groups.Select(y => y.Items.Select(z => z.Observations)),
    x => x.Groups.Select(y => y.Items.Select(z => z.Recommendations)),
    x => x.Checkins.Where(y => y.Complete==true),
    x => x.Checkins.Select(y => y.Inspections),
    x => x.Checkins.Select(y => y.Inspections.Select(z => z.InspectionItems))

});
var dealer = query.First();
return dealer;

Расширение можно найти по адресу thiscode / DynamicSelectExtensions на github

0 голосов
/ 15 июля 2011

@ Андреас Х:

Круто, большое спасибо.

Мне пришлось настроить ваше предложение следующим образом, и оно сработало:

var dealer = this.ObjectContext.Dealers
                    .Include("Groups")
                    .Include("Groups.Items")
                    .Include("Groups.Items.Observations")
                    .Include("Groups.Items.Recommendations")
                    .Where(p => p.DealerId == id).
                    FirstOrDefault();
        if (dealer != null)
        {
            this.ObjectContext.Checkins
                    .Include("Inspections")
                    .Include("Inspections.InspectionItems")
                   .Where(c => !c.Complete && c.Dealer.DealerId == dealer.DealerId)
                   .ToList();
        }
        return dealer;

У меня все еще естьчтобы использовать «Включить», в противном случае он не будет возвращать ссылочные объекты.

Обратите также внимание на то, что Dealer.Groups не связаны с Dealer.Checkins.

Поэтому, если нет проверок, удовлетворяющих условию, Группывсе еще нужно вернуть.

Интересно отметить, что сначала я поставил два ключа для проверки дилеру

var dealer = this.ObjectContext.Dealers
                        .Include("Groups")
                        .Include("Groups.Items")
                        .Include("Groups.Items.Observations")
                        .Include("Groups.Items.Recommendations")
                        .Include("Checkins.Inspections")
                        .Include("Checkins.Inspections.InspectionItems")
                        .Where(p => p.DealerId == id).
                        FirstOrDefault();
            if (dealer != null)
            {
                this.ObjectContext.Checkins
                        .Where(c => c.Complete && c.DealerId == id)
                       .ToList();
            }
            return dealer;

, но он возвратил все проверки, включая те, которые не являютсязавершено.

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

...