Пустое исключение LINQ-запроса, когда Where возвращает 0 строк - PullRequest
4 голосов
/ 28 сентября 2011

У меня есть следующий метод LINQ, который работает как положено, за исключением случаев, когда не найдено ни одной строки, тогда я получаю исключение Null.Я изо всех сил пытаюсь изменить это, чтобы вернуть 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)))
                            );
        }
    }

Спасибо


Я попробовал одно из предложений Джона Скита ниже, и теперь я получаю 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 ]

11 голосов
/ 28 сентября 2011

Вы используете

.Where(...)
.DefaultIfEmpty()

, что означает, что если результатов нет, представьте, что это последовательность с одним нулевым результатом.Затем вы пытаетесь использовать этот нулевой результат в вызове Max ...

Возможно, вы можете изменить его на:

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();
0 голосов
/ 13 октября 2016

У меня был удивительно похожий опыт работы с 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();
}

Единственным недостатком является то, что вы застряли со стандартным значением по умолчанию, вместо того, чтобы указать его.

0 голосов
/ 28 сентября 2011

В подобных ситуациях, когда может быть или не быть совпадающего элемента, я предпочитаю возвращать объект, а не тип значения. Если вы возвращаете тип значения, вы должны иметь некоторую семантику о том, что значение означает «здесь ничего нет». Я бы изменил его так, чтобы он возвращал последний счет-фактуру, а затем (если он не равен нулю) получит номер счета-фактуры из счета-фактуры. Добавьте метод в класс, чтобы вернуть числовой номер счета из строки.

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...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...