我有一个显示某个实体详细信息的网络应用,我们称之为Log
。该实体是通过Entity Framework 4从SQL Server加载的。
我想提供“下一个”和“上一个”链接,以双向浏览日志。
日志按两个属性/列排序:
Date
Time
这两个列都可能包含null
,并且不保证唯一性。如果这两个值都为null,那么为了保证稳定的排序,我按数据库Id
排序,这保证是非空且唯一的。
此外,在给定的Log
之前或之后实际上可能没有实体。
some其他questions可以直接使用SQL来解决这个问题。我想知道如何使用Entity Framework执行此操作,理想情况下只需要一次访问数据库就可以为这对Logs
(id,title等)带回几个字段。
答案 0 :(得分:1)
EF不支持Take
和Skip
吗?
LINQ的美妙之处在于,您可以通过说q.Skip(50).Take(50)
来描述这个复杂的排序标准和结果页面。如果每个页面显示50个结果,那么您将获得第二页。它当然被转换为有效的T-SQL,它使用ROW_NUMBER
窗口函数指示数据库使用您指定的顺序查找结果。
您甚至可以使用大量过滤器进行非常复杂的查询。最终结果仍然可以管理,因为你要么有行,要么你不会。你需要考虑的是结果可能是空的。
关于身份的说明,正如Ladislav指出的那样,在完全相同的排序键的条目之间不能保证顺序(即日期和时间都为空)。所以你要做的是添加一个标识列,这是你最不重要的排序列。没有标识的日志表/实体可能在某种程度上被认为设计不当,因为当日期和时间可以为空时,数据的增长是不可预测的。这将导致错误的页面拆分。经验法则是表应该具有狭窄且唯一的聚类主键。标识列很适合这个。它还将确保插入操作是您的日志表将会欣赏的快速操作。
在视图的帮助下,您可以将order by和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()
}
;
答案 1 :(得分:1)
我不确定这是否有效,但试一试:
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));
答案 2 :(得分:0)
这是我一直在使用的解决方案。理想情况下,我想通过一次数据库调用来做到这一点,所以如果有人能告诉我如何做到这一点,我会接受他们的回答。
// 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();
答案 3 :(得分:0)
将记录选择逻辑分别放入prev和next按钮。这样,您只需要为每次按钮单击调用数据库一次。