我想要包含一个子行,已过滤,并且只包含找到的最后一行。
我可以手动完成此操作效率低下如下:
var persons = dbContext.Persons.ToList();
foreach (var person in persons)
{
var lastMatchingNote = person.Notes
.Where(x => x.Type == 5)
.OrderBy(x => x.Date)
.LastOrDefault();
}
但这会重新查询每个人。
或者,我可以在原始查询中包含()注释,然后在内存中过滤:
var persons = dbContext.Persons.Include("Notes").ToList();
但是这会加载所有低效的音符。
在EF6中执行此操作的最佳方式是什么?
(存储过程,视图或其他奇特的解决方案是选项,但我更喜欢纯粹的LINQ到实体解决方案。创建计算字段也可以,如果我可以防止它被加载到涉及Person的所有其他查询。)
答案 0 :(得分:1)
尝试像这样查询数据库:
var results =
(from p in dbContext.Persons
select new {
person = p,
lastMatchingNote = p.Notes.Where(x => x.Type == 5)
.OrderByDescending(x => x.Date)
.First()
})
.ToList();
或者像这样:
var results =
(from p in dbContext.Persons
let lastMatchingNote = p.Notes.Where(x => x.Type == 5)
.OrderByDescending(x => x.Date)
.First()
select new { person = p, lastMatchingNote })
.ToList();
使用这种方法,您可以调整结果集中的列,从而更好地控制实际查询。
答案 1 :(得分:0)
最后,我决定为这个额外值创建一个单独的视图。这可能看起来有点过分,但性能非常出色,它可以检索单个查询中的所有值,考虑到大型数据集的性能要求,这是我方案中的优先级。
所以我有以下观点:
CREATE VIEW vwperson_lastproactivenote as
SELECT people.person_id AS id,
(SELECT max(notes.note_date) AS max
FROM notes
WHERE notes.person_id = people.person_id AND notes.note_type_id = 5) AS date
FROM people;
然后我可以构建一个与Person共享主键的模型:
public class PersonLastProactiveCall
{
[Required]
[ForeignKey("Id")]
public virtual Person Person { get; set; }
public DateTime? Date { get; set; }
}
...
// On person model
[ForeignKey("Id")]
public virtual PersonLastProactiveCall LastProactiveCall { get; set; }
所以我最终可以这样称呼它:
DataContext.Set<Person>().Include("LastProactiveCall").Where(...);
person.LastProactiveCall.Date // <-- Yay!!!!