Как выбрать следующие и предыдущие объекты с помощью Entity Framework? - PullRequest
2 голосов
/ 21 марта 2011

У меня есть веб-приложение, которое отображает детали некоторой сущности, назовем его Log. Сущность загружается из SQL Server через Entity Framework 4.

Я хотел бы предоставить ссылки «следующий» и «предыдущий» для двунаправленного просмотра журналов.

Журналы упорядочены по двум свойствам / столбцам:

  • Date
  • Time

Оба эти столбца могут содержать null, и нет никакой гарантии уникальности. Если оба эти значения равны нулю, то для обеспечения стабильной сортировки I порядок в базе данных Id, который гарантированно будет ненулевым и уникальным.

Кроме того, на самом деле не может быть сущности до или после данного Log.

Есть некоторые другие вопросы , которые обращаются к этому напрямую с помощью SQL. Я хотел бы знать, как это сделать с помощью Entity Framework, в идеале совершая всего одну поездку в БД, чтобы вернуть несколько полей для этой пары Logs (id, title и т. Д.).

Ответы [ 4 ]

1 голос
/ 21 марта 2011

Я не уверен, что это работает, но давайте попробуем:

var query =(
        from l in context.Logs
        where l.UserId == log.UserId &&
             (    l.Date < log.Date
              || (l.Date == log.Date && l.Time < log.Time)
              || (l.Date == log.Date && l.Time == log.Time && l.Id < log.Id)
             )
        orderby l.Date descending, l.Time descending
        select l
    ).Take(1)
    .Concat((
        from l in context.Logs
        where l.UserId == log.UserId &&
             (    l.Date > log.Date
              || (l.Date == log.Date && l.Time > log.Time)
              || (l.Date == log.Date && l.Time == log.Time && l.Id > log.Id)
             )
        orderby l.Date, l.Time
        select l
    ).Take(1));
1 голос
/ 21 марта 2011

EF не поддерживает Take и Skip?

Прелесть LINQ заключалась в том, что вы могли описать этот сложный критерий сортировки и просто отобразить результат, сказав q.Skip(50).Take(50).Это заняло бы вторую страницу, если бы на каждой странице отображалось 50 результатов.И, конечно, он переведен на эффективный T-SQL, который использует оконную функцию ROW_NUMBER, чтобы дать указание базе данных искать результат, используя указанный вами порядок.

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

Примечание об идентичности, как указал Ладислав, не гарантируется порядок между записями одного и того же ключа сортировки (т. Е. Date и Time оба равны нулю).).Итак, вы делаете, что вы добавляете столбец идентификации, который является вашим наименее важным столбцом сортировки.Журнальную таблицу / сущность, которая не имеет идентификатора, можно до некоторой степени утверждать, что она неправильно спроектирована, поскольку рост данных непредсказуем, когда дата и время могут быть равны нулю.Это приведет к плохим разбиениям страницы.Практическое правило заключается в том, что таблица должна иметь узкий и уникальный первичный ключ кластеризации.Столбец идентичности подходит для этого довольно хорошо.Это также гарантирует, что вставки - это быстрые операции, которые оценят ваши журнальные таблицы.

С помощью представления вы можете поместить элементы order и row_number в простой T-SQL, а затем запросить их с помощью EF следующим образом:

var q = from x in source
        join y in source on x.RowNumber equals y.RowNumber - 1 into prev
        join z in source on x.RowNumber equals z.RowNumber + 1 into next
        from p in prev.DefaultIfEmpty()
        from n in next.DefaultIfEmpty()
        select new { Current = x, Previous = p, Next = n }
        ;

... или, возможно:

var q = from x in source
        join y in source on x.RowNumber equals y.RowNumber - 1 into prev
        join z in source on x.RowNumber equals z.RowNumber + 1 into next
        select new { 
            Current = x, 
            Previous = prev.DefaultIfEmpty(), 
            Next = next.DefaultIfEmpty() 
        }
        ;
0 голосов
/ 14 марта 2013

Поместите логику выбора записи в кнопки назад и вперед по отдельности. Таким образом, вам нужно вызывать базу данных только один раз для каждого нажатия кнопки.

0 голосов
/ 21 марта 2011

Вот решение, с которым я работал.В идеале я хотел бы сделать это с помощью одного вызова БД, поэтому, если кто-нибудь сможет показать мне, как это сделать, я приму их ответ.

// Prev
var previousLog = (
        from l in context.Logs
        where l.UserId == log.UserId &&
             (    l.Date < log.Date
              || (l.Date == log.Date && l.Time < log.Time)
              || (l.Date == log.Date && l.Time == log.Time && l.Id < log.Id)
             )
        orderby l.Date descending, l.Time descending
        select l
    ).FirstOrDefault();

// Next
var nextLog = (
    from l in context.Logs
    where l.UserId == log.UserId &&
             (    l.Date > log.Date
              || (l.Date == log.Date && l.Time > log.Time)
              || (l.Date == log.Date && l.Time == log.Time && l.Id > log.Id)
             )
    orderby l.Date, l.Time
    select l
).FirstOrDefault();
...