LINQ Max()与Nulls

Jas*_*son 51 c# linq

我有一个包含一堆Points(带有X和Y组件)的列表.

我想获得列表中所有点的Max X,如下所示:

double max = pointList.Max(p=> p.X);
Run Code Online (Sandbox Code Playgroud)

问题是当我在列表中有一个null而不是一个点.解决这个问题的最佳方法是什么?

Ani*_*Ani 107

好吧,你可以把它们过滤掉:

pointList.Where(p => p != null).Max(p => p.X)
Run Code Online (Sandbox Code Playgroud)

另一方面,如果您希望将nulls视为具有X坐标0(或类似)的点,则可以执行以下操作:

pointList.Max(p => p == null ? 0 : p.X)
Run Code Online (Sandbox Code Playgroud)

请注意,如果序列为空,则两种技术都会抛出.一个解决方法(如果需要)将是:

pointList.DefaultIfEmpty().Max(p => p == null ? 0 : p.X)
Run Code Online (Sandbox Code Playgroud)

  • 很好,谢谢 (5认同)

Mar*_*ers 27

如果要为空点的X提供默认值:

pointList.Max(p => p == null ? 0 : p.X)
Run Code Online (Sandbox Code Playgroud)

或者为空列表提供默认值:

int max = points.Where(p => p != null)
                .Select(p => p.X)
                .DefaultIfEmpty()
                .Max();
Run Code Online (Sandbox Code Playgroud)


Nik*_*ann 14

我不建议DefaultIfEmpty在这种情况下使用,因为与其他替代方案相比,它产生了相当大的SQL.

请看这个例子:

我们有一个页面模块列表,并希望获得"排序"列的最大值.如果列表没有记录,则返回null.DefaultIfEmpty检查null并在列为null时返回列数据类型的默认值.

var max = db.PageModules.Where(t => t.PageId == id).Select(t => t.Sort).DefaultIfEmpty().Max();
Run Code Online (Sandbox Code Playgroud)

这会产生以下SQL:

exec sp_executesql N'SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    MAX([Join1].[A1]) AS [A1]
    FROM ( SELECT 
        CASE WHEN ([Project1].[C1] IS NULL) THEN 0 ELSE [Project1].[Sort] END AS [A1]
        FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
        LEFT OUTER JOIN  (SELECT 
            [Extent1].[Sort] AS [Sort], 
            cast(1 as tinyint) AS [C1]
            FROM [dbo].[PageModules] AS [Extent1]
            WHERE [Extent1].[PageId] = @p__linq__0 ) AS [Project1] ON 1 = 1
    )  AS [Join1]
)  AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=11
go
Run Code Online (Sandbox Code Playgroud)

如果我们改为将列转换为可空列,并按如下方式Convert.ToInt32()处理null:

var max = Convert.ToInt32(db.PageModules.Where(t => t.PageId == id).Max(t => (int?)t.Sort));
Run Code Online (Sandbox Code Playgroud)

然后我们得到以下SQL:

exec sp_executesql N'SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    MAX([Extent1].[Sort]) AS [A1]
    FROM [dbo].[PageModules] AS [Extent1]
    WHERE [Extent1].[PageId] = @p__linq__0
)  AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=11
go
Run Code Online (Sandbox Code Playgroud)

我真的可以推荐使用ExpressProfiler来检查执行的SQL:http: //expressprofiler.codeplex.com/

最后一个Linq表达式也可以写成:

var max = Convert.ToInt32(db.PageModules.Where(t => t.PageId == id).Select(t => (int?)t.Sort).Max());
Run Code Online (Sandbox Code Playgroud)

并将生成相同的SQL但我更喜欢更简洁.Max(t => (int?)t.Sort).

  • 任何为您生成SQL的ORM或工具的问题在于,它们使脚下射击变得如此容易。您只需要处理代码,几乎没有人关注生成的SQL。这些机械翻译的问题在于它们会产生“一种尺寸适合所有人”的代码,有时,它比您可能需要的要大得多。感谢您在这篇文章中强调这一点。最重要的是,请务必检查您生成的查询!+1! (2认同)

Car*_*lin 5

在表达式内部放置可为 null 的强制转换,以确保空白列表将强制转换为 null。然后您可以添加默认值。

double max = pointList.Max(p=>(double?)p.X) ?? 0;
Run Code Online (Sandbox Code Playgroud)