Linq查询返回父项或最近的子项

时间:2013-04-03 15:26:58

标签: linq entity-framework

我有一个引用自己的类,如下所示:

public class Person
{
    int Id { get; set; }
    string Name { get; set; }
    DateTime CreatedOn { get; set; }

    Person Parent { get; set; }
    ICollection<Person> Children { get; set; }
}

我需要的是一个Linq查询,它将返回一个列表,该列表仅包含具有子项的记录的最新记录(基于“CreatedOn”),或者为那些不具有子项的记录返回父本身。该查询还需要对整个“族”应用一些过滤器。例如,如果我使用名称“John”进行过滤,并且只有父级的名字是“John”,我仍然需要检索其最近的孩子。

任何帮助都将不胜感激。

4 个答案:

答案 0 :(得分:1)

你可以这样做:

var results = db.Persons.Where(p => p.Name == "John")
                        .Select(p => p.Children.OrderByDescending(c => c.CreadedOn).FirstOrDefault() ?? p);

或者如果您更喜欢查询语法

var results = 
    from p in db.Persons
    where p.Name == "John"
    select(p => p.Children.OrderByDescending(c => c.CreadedOn).FirstOrDefault() ?? p);

生成的sql将如下所示:

SELECT 
    (CASE 
        WHEN NOT (EXISTS(
            SELECT TOP (1) NULL AS [EMPTY]
            FROM [Person] AS [t2]
            WHERE [t2].[ParentId] = [t0].[Id]
            ORDER BY [t2].[Id] DESC
            )) THEN 1
        WHEN NOT NOT (EXISTS(
            SELECT TOP (1) NULL AS [EMPTY]
            FROM [Person] AS [t2]
            WHERE [t2].[ParentId] = [t0].[Id]
            ORDER BY [t2].[Id] DESC
            )) THEN 0
        ELSE NULL
     END) AS [value], [t0].[Id], [t0].[ParentId], [t0].[Name], [t0].[CreatedOn]
FROM [Parent] AS [t0]
WHERE [t0].[Name] = @p0
GO

答案 1 :(得分:1)

对于Linq-To-Entities不确定,Linq-To-Objects应该可以工作:

var lastChildOfJohn = persons.Where(p => p.Name == "John" && p.Children.Any())
    .SelectMany(p => p.Children)
    .OrderByDescending(p => p.CreatedOn)
    .FirstOrDefault(); 

答案 2 :(得分:0)

你应该使用 Stack 对象,因为它们可以在LIFO(后进先出基础)上工作。

以下是MSDN的链接: -

http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx

对我来说,这正是你想要做的。

如果使用Stack.Peek方法,它将自动返回放置在堆栈上的最后一个对象。

您还可以使用Pop返回最新的对象,然后将其从堆栈中删除。

  

Peek返回堆栈顶部的对象而不删除它。

     

Pop删除并返回堆栈顶部的对象。

答案 3 :(得分:0)

好吧,所以经过大量的搔抓和反复试验,这是我提出的查询(分为3行以便于阅读):

var query = db.People.AsQueryable();
query = !string.IsNullOrEmpty(name) ? query.Where(x => x.Name.Contains(name) || x.Ocorrencias.Any(y => y.Name.Contains(name))) : query;
query = query.Select(p => (p.Children.Count == 0 && p.Parent == null) ? p : p.Children.OrderByDescending(c => c.CreatedOn).FirstOrDefault()).Where(x => x != null);

这将返回我需要的内容:将过滤器应用于父级和子级的对象列表(如果有)。如果有孩子,只返回最近的孩子。我不得不在末尾添加“Where x!= null”来过滤掉“FirstOrDefault”返回的空对象。

我打算将p.s.w.g和Tim的评论标记为答案,因为他们让我走上了正确的轨道来解决这个问题,但显然我只能将一条评论标记为答案。谢谢你的帮助!