如何在EF6中包含最后一个,已过滤,已排序的子行

时间:2014-01-12 23:48:13

标签: c# entity-framework linq-to-entities domain-driven-design

我想要包含一个子行,已过滤,并且只包含找到的最后一行。

我可以手动完成此操作效率低下如下:

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的所有其他查询。)

2 个答案:

答案 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!!!!