当Where返回0行时,LINQ查询null异常

时间:2011-09-28 16:24:39

标签: c# .net linq linq-to-sql .net-3.5

我有以下LINQ方法按预期工作,除非找到No Rows,然后我得到Null Exception。我正在努力如何修改它以返回0,如果发生这种情况。

    public static int GetLastInvoiceNumber(int empNumber)
    {
        using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
        {
            context.Log = Console.Out;

            IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();

            return (tGreenSheet
                            .Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
                            .DefaultIfEmpty()
                            .Max(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
                            );
        }
    }

由于


我在下面尝试了Jon Skeet的一个建议,现在我得到了Unsupported overload used for query operator 'DefaultIfEmpty'

    public static int GetLastInvoiceNumber(int empNumber)
    {
        using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
        {
            context.Log = Console.Out;

            IQueryable<tblGreenSheet> tGreenSheet = context.GetTable<tblGreenSheet>();

            return tGreenSheet
                            .Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
                            .Select(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
                            .DefaultIfEmpty(0)
                            .Max();                                
        }
    }

3 个答案:

答案 0 :(得分:11)

你正在使用

.Where(...)
.DefaultIfEmpty()

表示如果没有结果,则假装它是具有单个null结果的序列。然后,您尝试在Max调用中使用该null结果...

您可以将其更改为:

return tGreenSheet.Where(gs => ...)
                  .Max(gs => (int?) Convert.ToInt32(...)) ?? 0;

这使用重载找到int?值的最大值 - 如果没有值,则返回int? null?? 0然后将该空值转换为0.至少,这是LINQ to Objects行为......您必须检查它是否为您提供相同的结果。

当然,如果您愿意更改方法签名以返回?? 0,则无需使用int?。这将提供额外的信息,因为调用者可以告诉“无数据”和“最大值为0的某些数据”之间的区别:

return tGreenSheet.Where(gs => ...)
                  .Max(gs => (int?) Convert.ToInt32(...));

另一种选择是使用DefaultIfEmpty()的重载来获取值 - 如下所示:

return tGreenSheet.Where(gs => ...)
                  .Select(gs => Convert.ToInt32(...))
                  .DefaultIfEmpty(0)
                  .Max();

答案 1 :(得分:0)

在这种情况下,当可能存在或不存在匹配项时,我更喜欢返回对象而不是值类型。如果你返回一个值类型,你必须有一些关于什么值意味着“这里没有任何东西”的语义。我会将其更改为返回最后一张发票,然后(当它为非空时)从发票中获取发票号。在类中添加一个方法,以便从字符串中返回数字发票号。

public static tbleGreenSheet GetLastInvoice(int empNumber)
{
    using (var context = new CmoDataContext(Settings.Default.LaCrosse_CMOConnectionString))
    {
        context.Log = Console.Out;

        return context.GetTable<tblGreenSheet>()
                      .Where(gs => gs.InvoiceNumber.Substring(2, 4) == empNumber.ToString())
                      .OrderByDescending(gs => Convert.ToInt32(gs.InvoiceNumber.Substring(6, gs.InvoiceNumber.Length)))
                      .FirstOrDefault();
    }
}

public class tbleGreenSheet
{
    ....
    public int NumericInvoice
    {
        get { return Convert.ToInt32(InvoiceNumber.Substring(6, InvoiceNumber.Length)); }
    }
    ...
}

用作

var invoice = Foo.GetLastInvoice( 32 );
if (invoice != null)
{
     var invoiceNumber = invoice.NumericInvoice;

     ...do something...
}
else
{
     ...do something else...
}

答案 2 :(得分:0)

我与IQueryable<T>和NHibernate的体验非常相似。我的解决方案:

public static TExpr MaxOrDefault<TItem, TExpr>(this IQueryable<TItem> query,
                                               Expression<Func<TItem, TExpr>> expression) {
  return query.OrderByDescending(expression).Select(expression).FirstOrDefault();
}

唯一的缺点是你被标准的默认值所困,而不是指定一个。