【问题标题】:FirstOrDefault() when retrieving a single field value检索单个字段值时的 FirstOrDefault()
【发布时间】:2013-10-20 22:13:41
【问题描述】:

这种事情一直困扰着我,所以我想我应该向你们这些聪明人寻求“更好的方法”。

我有一个查询,我想返回单个字段的值,这是一个 int 值。

int thivalue = (from q in context.tablename    
    where q.ID == id
    orderby q.fieldname descending
    select q).FirstOrDefault().fieldname;

问题是查询可能不返回任何结果,在这种情况下我希望 thisvalue 为 0。

当然,如果没有结果,我会在尝试访问不存在的字段时遇到异常。所以看来我的选择是 a)返回行(我不需要),所以我可以测试 null 并从那里开始,或者 b) 在它周围包裹一个 try catch 并将值设置为 0,这看起来有点笨拙。

我认为 DefaultIfEmpty() 可能会对我有所帮助,但如果我想要的只是一个单一的值,这似乎不是。

那么正确的方法是什么?当我只需要一个值时,我是否只是固执地不想返回整行?

附录:(如果有人感兴趣)

在他的回答中,berkeleybross 给了我两个明显相同的选择。但只有第二个给了我正确的结果。 OrderByDescending 似乎有些不对劲。使用 Glimpse 我查看了每个查询。

    var nextSequence =  db.PaneContents
                       .Where(q=>q.QuizPaneID == quizPaneId)
                       .OrderByDescending(q=>q.Sequence)
                       .Select (q=>q.Sequence)
                       .DefaultIfEmpty()
                       .First();

产生了这个查询:

SELECT 
CASE WHEN ([Limit1].[C1] IS NULL) THEN 0 ELSE [Limit1].[Sequence] END AS [C1]
FROM ( SELECT TOP (1) 
    [Project1].[Sequence] AS [Sequence], 
    [Project1].[C1] AS [C1]
    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
    LEFT OUTER JOIN  (SELECT 
        [Extent1].[Sequence] AS [Sequence], 
        cast(1 as tinyint) AS [C1]
        FROM [dbo].[PaneContents] AS [Extent1]
        WHERE [Extent1].[QuizPaneID] = 274 /* @p__linq__0 */ ) AS [Project1] ON 1 = 1
)  AS [Limit1]

var nextSequence = (from q in db.PaneContents
                                where q.QuizPaneID == quizPaneId
                                orderby q.Sequence descending
                                select q.Sequence).FirstOrDefault();

产生了这个查询:

SELECT TOP (1) 
[Project1].[Sequence] AS [Sequence]
FROM ( SELECT 
    [Extent1].[Sequence] AS [Sequence]
    FROM [dbo].[PaneContents] AS [Extent1]
    WHERE [Extent1].[QuizPaneID] = 274 /* @p__linq__0 */
)  AS [Project1]
ORDER BY [Project1].[Sequence] DESC

在第一个示例中,OrderByDescending() 似乎在翻译中迷路了? 无论如何,出于我的目的,不需要 DefaultIfEmpty(),第二个示例更简单。我只是觉得它不起作用很奇怪(“不起作用”是指它选择了错误的值,因为它没有按降序排序),因为它看起来应该如此。

【问题讨论】:

    标签: asp.net-mvc-4 entity-framework-4 dbcontext


    【解决方案1】:

    如果我理解正确,你可以试试这个:

    int thisvalue = context.tablename
                           .Where(q => q.ID == id)
                           .OrderByDescending(q => q.fieldname)
                           .Select(q => q.fieldname)
                           .FirstOrDefault();
    

    int thisvalue = (from q in context.tablename    
                     where q.ID == id
                     orderby q.fieldname descending
                     select q.fieldname).FirstOrDefault();
    

    在调用 FirstOrDefault() 之前选择 q.fieldname 将使 SQL 只返回第一项字段名(而不是整个第一行)。


    另外,q.ID 是唯一的吗?如果是这样,那么您不需要按字段名排序,您可以使用 SingleOrDefault 而不是 FirstOrDefault。 Single 确保只有一个元素,而 First 确保至少有一个。应该使用 Single 来说明您的假设,它对于更多的自记录代码是唯一的。

    int thisvalue = context.tablename
                           .Where(q => q.ID == id)
                           .Select(q => q.fieldname)
                           .SingleOrDefault();
    

    【讨论】:

    • 这看起来是正确的,奇怪的是 OrderByDescending 在这种情况下似乎没有达到预期的效果。如果我在没有 .First() 的情况下进行调试,我会得到一个升序列表......仍在使用它。
    • 回答您的最后一个问题,q.ID 不是唯一的。这些是详细记录,我正在检索“fieldname”的最大值(用于排序目的的序列号),然后我将其递增并分配给新记录
    • 你的第二个例子正是我想要的。它们看起来功能相同,不知道为什么第一个不起作用。
    【解决方案2】:

    我会做这样的事情

    int thisvalue = context.tablename
        .Where(q => q.ID == id)
        .OrderByDescending(q => q.fieldname)
        .ToList()
        .DefaultIfEmpty(new myObject {fieldname = 0})
        .FirstOrDefault().fieldname;
    

    【讨论】:

    • 感谢您的建议,我首先看到了 berkeleybross 的回答,并且它更接近于我的开始。我喜欢 DefaultIfEmpty 的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-03
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多