转换Select以从集合中的.Last()获取属性

时间:2016-09-22 19:39:53

标签: c# entity-framework entity-framework-6

我有一个有许多孩子的复杂对象,我试图只从中选择一些属性及其子项以在网格中显示。之前通过盲目地抛出所需的每个.Include()来构建查询的方式生成了一个1095行的SQL语句。

从子对象获取单个属性没有问题,但是这个属性是执行的最后一个活动的名称。对集合执行.Last().Name会抛出一个异常,即它无法转换为SQL。我将提供一个基本示例来帮助可视化(所有FK实际上都在我的代码中设置,这不是问题):

public class Foo
{
    public int Id { get; set; }
    // just a dummy class everyone knows for illustration
    public Address Address { get; set; }
    public ICollection<Activity> Activities { get; set; }
}

public class Activity
{
    public string Name { get; set; }
}

public class FooModel
{
    public int Id { get; set; }
    public string StreetName { get; set; }
    public string LastActivity { get; set; }
}

这是我设置的查询的基本示例:

public IEnumerable<FooModel> GetHomePageItems(IEnumerable<int> fooIds)
{
    return await context.Foos
                        .Where(f => fooIds.Contains(f.id))
                        .Select(f => new FooModel
                            {
                                Id = f.Id,
                                StreetName = f.Address.Street,
                                // here is the problem as it can't 
                                // convert this to SQL
                                LastActivity = f.Activities.Last().Name
                            })
                        .ToListAsync();
}

这件事是否可以完成,或者我是否必须在没有LastActivity的情况下完成任务,然后使用GroupBy查询活动并以这种方式获取它们?

1 个答案:

答案 0 :(得分:3)

您可以尝试将查询更改为:

var query = context.Foos
                .Where(f => fooIds.Contains(f.Id))
                .Select(f => new FooModel
                {
                    Id = f.Id,
                    StreetName = f.Address.Street,     
                    LastActivity = f.Activities.OrderByDescending(x => x.Id).FirstOrDefault().Name
                }).ToListAsync();

linq为Entity Framework V6.1.3生成以下sql:

SELECT
    [Filter1].[Id1] AS [Id],
    [Filter1].[Street] AS [Street],
    [Limit1].[Name] AS [Name]
    FROM   (SELECT [Extent1].[Id] AS [Id1], [Extent2].[Street] AS [Street]
        FROM  [dbo].[Foos] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Addresses] AS [Extent2] ON [Extent1].[Address_Id] = [Extent2].[Id]
        WHERE [Extent1].[Id] IN (1, 2, 3, 4) ) AS [Filter1]
    OUTER APPLY  (SELECT TOP (1) [Project1].[Name] AS [Name]
        FROM ( SELECT
            [Extent3].[Id] AS [Id],
            [Extent3].[Name] AS [Name]
            FROM [dbo].[Activities] AS [Extent3]
            WHERE [Filter1].[Id1] = [Extent3].[Foo_Id]
        )  AS [Project1]
        ORDER BY [Project1].[Id] DESC ) AS [Limit1]

这对您的项目来说可能已经足够了。但是,在大量数据上,您可能需要更快地切换到某些内容,甚至可能使用.Sql()方法进行手动查询。

实体框架无法识别.LastOrDefault().Last()方法,而应使用.FirstOrDefault().First()方法与OrderBy()OrderByDescending()相结合