Используя LINQ для поиска элемента в списке, но получите "Значение не может быть нулевым. Имя параметра: источник"

При использовании LINQ для получения данных из списка я сталкиваюсь с этой ошибкой. Как это можно зафиксировать?

Значение не может быть нулевым. Имя параметра: источник

var nCounts = from sale in sal
              select new
              {
                  SaleID = sale.OrderID,
                  LineItem = from sli in sale.LineItems
                             group sli by sli.Item into ItemGroup
                             select new
                             {
                                 Item = ItemGroup.Key,
                                 Weeks = ItemGroup.Select(s => s.Week)
                             }
              };

foreach (var item in nCounts)
{
    foreach (var itmss in item.LineItem)
    {
        // MessageBox.Show(itmss.Item.Name);
        itemsal.Add(new Roundsman.BAL.WeeklyStockList(itmss.Item.Name.ToString(),
                    itmss.Item.Code.ToString(),
                    itmss.Item.Description.ToString(),
                    Convert.ToInt32(itmss.Item.Quantity), 2, 2, 2, 2, 2, 2, 2, 2, 2));
    }                      
}

Ошибка, которую я получил после выполнения моего LINQ Я получил такой результат (одна строка, оригинально):

System.Linq.Enumerable.WhereSelectListIterator < Roundsman.BAL.Sale,     < > f__AnonymousType1 < INT, System.Collections.Generic.IEnumerable     < < > f__AnonymousType0 < Roundsman.BAL.Items.Item, System.Collections.Generic.IEnumerable     < Roundsman.BAL.WeeklyRecord → → >

Ответ 1

Ошибка, которую вы получаете, отличается от того, который вы показываете здесь. Это метод, который принимает параметр с именем "источник". В диалоговом окне "Параметры Visual Studio" отключите "Только мой код", отключите "Шаг над свойствами и операторы" и включите "Включить исходный шаг .NET Framework". Убедитесь, что символы .NET можно найти. Тогда отладчик будет ломаться внутри метода .NET, если он не является вашим собственным. затем проверьте stacktrace, чтобы узнать, какое значение передано с нулевым значением, но не должно.

То, что вам нужно искать, - это значение, которое становится null и предотвращает это. Если вы посмотрите на свой код, это может быть разрыв строки itemsal.Add.

Изменить

Поскольку у вас, похоже, проблемы с отладкой вообще и LINQ, особенно, попробуйте помочь вам шаг за шагом (также обратите внимание на расширенный первый раздел выше, если вы все еще хотите попробовать классический способ, я не был полностью в первый раз):

  • Сократите возможные сценарии ошибок, разделив код;
  • Замените места, в которых может null быть что-то намеренно не null;
  • Если все не удается, перепишите оператор LINQ как цикл и пройдите по нему шаг за шагом.

Шаг 1

Сначала сделайте код более читаемым, разделив его на управляемые части:

// in your using-section, add this:
using Roundsman.BAL;

// keep this in your normal location
var nCounts = from sale in sal
              select new
              {
                  SaleID = sale.OrderID,
                  LineItem = GetLineItem(sale.LineItems)
              };

foreach (var item in nCounts)
{
    foreach (var itmss in item.LineItem)
    {
        itemsal.Add(CreateWeeklyStockList(itmss));
    }
}


// add this as method somewhere
WeeklyStockList CreateWeeklyStockList(LineItem lineItem)
{
    string name = itmss.Item.Name.ToString();  // isn't Name already a string?
    string code = itmss.Item.Code.ToString();  // isn't Code already a string?
    string description = itmss.Item.Description.ToString();  // isn't Description already a string?
    int quantity = Convert.ToInt32(itmss.Item.Quantity); // wouldn't (int) or "as int" be enough?

    return new WeeklyStockList(
                 name, 
                 code, 
                 description,
                 quantity, 
                 2, 2, 2, 2, 2, 2, 2, 2, 2
              );
}

// also add this as a method
LineItem GetLineItem(IEnumerable<LineItem> lineItems)
{
    // add a null-check
    if(lineItems == null)
        throw new ArgumentNullException("lineItems", "Argument cannot be null!");

    // your original code
    from sli in lineItems
    group sli by sli.Item into ItemGroup
    select new
    {
        Item = ItemGroup.Key,
        Weeks = ItemGroup.Select(s => s.Week)
    }
}

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

Шаг 2

Следующим шагом является тщательная замена каждой части, которая может привести к исключению нулевой ссылки. Под этим я подразумеваю, что вы заменяете это:

select new
{
    SaleID = sale.OrderID,
    LineItem = GetLineItem(sale.LineItems)
};

с чем-то вроде этого:

select new
{
    SaleID = 123,
    LineItem = GetLineItem(new LineItem(/*ctor params for empty lineitem here*/))
};

Это создаст выход мусора, но сужает проблему еще дальше до вашей потенциальной линии нарушения. Сделайте то же, что и выше для других мест в операторах LINQ, которые могут закончиться null (почти обо всем).

Шаг 3

Этот шаг вам нужно будет сделать сам. Но если LINQ терпит неудачу и дает вам такие головные боли и такой нечитаемый или трудно отлаживаемый код, подумайте о том, что произойдет со следующей проблемой, с которой вы столкнулись? И что, если он терпит неудачу в живой среде, и вы должны решить его под давлением времени =

Мораль: всегда хорошо учиться новым приемам, но иногда даже лучше вернуться к чему-то понятному и понятному. Ничего против LINQ, мне это нравится, но в этом конкретном случае дайте ему отдохнуть, исправьте его простым циклом и переименуйте его через полгода или около того.

Заключение

Собственно, нечего заключать. Я пошел немного дальше, чем обычно, с длинным расширенным ответом. Я просто надеюсь, что это поможет вам лучше решить проблему и даст вам некоторые инструменты, чтобы понять, как вы можете сузить трудно-отладочные ситуации, даже без усовершенствованных методов отладки (которые мы не обсуждали).

Ответ 2

Это исключение может указывать на параметр LINQ, названный источником:

 System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)

Поскольку параметр source в вашем запросе LINQ (var nCounts = from sale in sal) равен 'sal', я полагаю, что список с именем "sal" может иметь значение null.

Ответ 3

Я думаю, вы можете получить эту ошибку, если ваша модель базы данных неверна, а базовые данные содержат нуль, который модель пытается сопоставить с ненулевым объектом.

Например, некоторые автогенерированные модели могут попытаться сопоставить столбцы nvarchar (1) с char, а не строку, и, следовательно, если этот столбец содержит значения null, он будет вызывать ошибку при попытке доступа к данным.

Примечание. LinqPad имеет параметр совместимости, если вы хотите, чтобы он создал такую ​​модель, но, вероятно, не делает этого по умолчанию, что может объяснить, что это не дает вам ошибки.

Ответ 4

Хорошо - мое дело не было строго связано с этим вопросом, но я попал на эту страницу, когда Googling для того же исключения; Я запросил базу данных SQL с LINQ, когда получил это исключение.

private Table<SubscriptionModel> m_subscriptionsSql;
var query = from SubscriptionModel subscription in m_subscriptionsSql ...

Оказалось, что я забыл инициализировать мою переменную экземпляра таблицы, которую я использовал в запросе LINQ.

m_subscriptionsSql = GetTable<SubscriptionModel>();

Ответ 5

Вот примеры кода, которые приведут к исключению аргумента null:

List<Myobj> myList = null;
//from this point on, any linq statement you perform on myList will throw an argument null exception
myList.ToList();
myList.GroupBy(m => m.Id);
myList.Count();
myList.Where(m => m.Id == 0);
myList.Select(m => m.Id == 0);
//etc...

Ответ 6

Эта ошибка может возникать в нескольких местах, чаще всего выполняется дальнейшие запросы LINQ поверх пустой коллекции. LINQ как Синтаксис запроса может казаться более нулевым, чем он есть. Рассмотрим следующие примеры:

var filteredCollection = from item in getMyCollection()
                         orderby item.ReportDate
                         select item;

Этот код не является NULL SAFE, а это означает, что если getMyCollection() возвращает null, вы получите ошибку Value cannot be null. Parameter name: source. Очень надоедливый! Но это имеет смысл, потому что синтаксис LINQ Query является синтаксическим сахаром для этого эквивалентного кода:

var filteredCollection = getMyCollection().OrderBy(x => x.ReportDate);

Который, очевидно, взорвется, если исходный метод возвращает нуль.

Чтобы предотвратить это, вы можете использовать оператор нулевой коалесценции в вашем запросе LINQ следующим образом:

var filteredCollection = from item in getMyCollection() ?? 
                              Enumerable.Empty<CollectionItemClass>()
                         orderby item.ReportDate
                         select item;

Однако вы должны будете помнить об этом в любых связанных запросах. Лучший подход (если вы управляете кодом, который генерирует коллекцию), должен сделать его практикой кодирования, НИКОГДА НЕ ВОЗВРАЩАЙТЕ СЛЕДУЮЩУЮ КОЛЛЕКЦИЮ, КОГДА-ЛИБО. В некоторых случаях возврат нулевого объекта из метода типа "getCustomerById(string id)" в порядке, в зависимости от стиля командной строки, но если у вас есть метод, который возвращает коллекцию бизнес-объектов, например "getAllcustomers()", тогда он должен НИКОГДА не возвращайте нулевой массив/перечисляемый/etc. Всегда всегда используйте проверку if, оператор нулевой коалесценции или какой-либо другой переключатель, чтобы возвращать пустой массив/список/перечисляемый и т.д., чтобы потребители вашего метода могли свободно LINQ над результатами.