2015-03-12 4 views
3

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

Я искать вокруг и можно увидеть это исключение возникает при попытке доступа или использования агрегата в пустом списке.

Я искал код вокруг этого исключения (слишком плохое не было зарегистрировано в стеке), и единственным «потенциальным» виновником являются следующие строки (которые используют либо Fist(), Last(), Single(), либо любую совокупность). Однако я не могу понять, почему и не способен воспроизвести на моем местном. Пожалуйста, помогите советовать.

if (data.Any()) 
    return data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime; 

Здесь data является List<MyObject> и MyObject имеет DateTime свойство называется UpdatedTime

===== больше окружающих код =====

Это где я получил необработанное исключение в журнал. Метод GetRecentUpdates имеет свой собственный блок catch catch, поэтому исключается.

public ActionResult GetUpdatedTime(long lastUpdated) { 
    var data = dataAccess.GetRecentUpdates(lastUpdated); 
    var html = htmlBuilder.Build(data); 
    return Content(html); 
} 

public List<MyObject> GetRecentUpdates(long lastUpdatedInTicks) { 
    var list = _cache.GetRecentRequests(_userCache.UserId); 
    if (list != null) { 
     var lastUpdated = new DateTime(lastUpdatedInTicks); 
     list = list.Where(l => l!=null && l.UpdatedTime > lastUpdated).ToList(); 
    } 
    return list ?? new List<MyObject>(); 
} 

public List<MyObject> GetRecentRequests(string userId) { 
    List<MyObject> requests = null; 
    try { 
     // simplied but the idea stays 
     requests = dictionary.Get(userId); 
     commonRequests = dictionary.Get("common"); 

     if (requests != null) { 
      if (commonRequests != null) 
       requests = requests.Union(commonRequests).ToList(); 
     } else { 
      request = commonRequests; 
     } 

     if (requests != null) { 
      requests = requests.OrderByDescending(r => r.CreatedDateTime).ToList(); 
    } 
    catch (Exception ex) { 
     // log the exception (handled) 
    } 

    return requests; 
} 

public string Build(List<MyObject> data) { 
    var lastUpdated = DateTime.MinValue; 
    if (data.Any()) 
     lastUpdated = data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime; 
    return String.Format("<tr style=\"display:none\"><td><div Id='MetaInfo' data-lastUpdated='{0}' /></td></tr>", lastUpdated.Ticks); 
} 

Javascript называет GetUpdatedTime каждые 10 сек. Обычно все идет хорошо, но каждый раз в какое-то время это исключение бросается. Как только он брошен, он постоянно бросается каждые 10 секунд, пока пользователь не обновит страницу.

+6

Выполняет ли этот код в многопоточной среде? – user3596113

+0

Да, но 'data' - локальная переменная внутри метода. – harry

+5

Вы покажете больше, чтобы найти код этого номера? –

ответ

1

Update:

Другой вариант после того, как некоторые исследования: как вы сказали, что ваш код работает в multhithreading среде, а data объект может получить доступ к двум или более нитей. Поскольку это переменная типа reference, ссылка на нее может быть изменена. Итак, рассмотрим такую ​​ситуацию:

Первый поток входит метод Build и проверки состояния:

if (data.Any()) 

и data не является пустым в этот момент, так что он входит в true блок. Правильно именно за это время еще одна нить входит в метод Build, но в этот момент переменная data пуста, и вся ссылка на нее указывает на пустой List. Но первая одна нить уже в true блока:

lastUpdated = data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime; 

И он не с вами исключение. И теперь хорошие новости: вы можете исправить это разными способами:

  • Прежде всего, проверьте создание логики data. Может быть, это статическая или общая переменная или объект, из которой она заполняется, является статической или общей переменной, и у вас есть условие гонки для этого ресурса. Вы можете изменить логику его создания или перенести его на некоторый синхронизирующий примитив, так что только один поток может Build одновременно (, но это может повлиять на производительность вашей программы).
  • Измените логику GetRecentRequests - не могу сказать точно, но я думаю, что ситуация примерно такая: commonRequests все время пустые, и для первого потока dictionary получили некоторые данные, но не имеют данных для второго thread и data объект переопределяется и пуст. Способ его отладки: добавьте Barrier примитив к вашей программе во время тестового прогона и подождите 10-15 потоков, ожидающих барьера. После этого они начнут одновременно создавать ваши данные, и с большой вероятностью произойдет ошибка (не вставляйте точки останова - они синхронизируют ваши потоки).
  • сделать локальную копию data объекта, что-то вроде этого:

    var localData = data.Select(d => d).ToList(); 
    

Надеется, что это помогает.


Ваш код проверяет наличие некоторых данных и после этого фильтрует данные по дате. Как вы используете методы расширения LINQ, я думаю, что data является IEnumerable объекта, а не List, поэтому, когда вы звоните Any() метод, это время перечислены, и после этого, вы вызываете First() метод, который перечисление тоже.

Итак, если ваши данные являются результатом некоторого метода yeild return, он переименовывается один раз, а во второй раз там нет данных, а последовательность пуста.

Рассмотрим изменить свой код для работы с данными в качестве List или Array, или использовать метод FirstOrDefault иметь null объект, если нет данных, как это:

//var dataList = data.OrderByDescending(d => d.UpdatedTime).ToList(); 
if (data.Count > 0) 
    return dataList[0].UpdatedTime; 

или

var firstElement = data.OrderByDescending(d => d.UpdatedTime).FirstOrDefault(); 
return firstElement != null ? firstElement.UpdatedTime : DateTime.MinValue; 
+0

Не должно быть проблем, если данные являются списком , хотя –

+0

@FlorianSchmidinger может быть проблемой, если List вычисляется вне метода с помощью 'yield', если он привязан к некоторым данным события. Одно событие - один элемент списка, а не два. – VMAtm

+0

После того, как я назвал 'data.Any()', я назвал 'OrderByDescending()', поэтому на самом деле это не фильтр. А также 'dataAccess.GetRecentUpdates (lastUpdated)' возвращает список harry

0

Ваш код проверяет сначала, если какой-либо элемент коллекции data удовлетворяет предикату с data.Any(). Поскольку предикат не существует, этот вызов эквивалентен тому, чтобы сообщить вам, есть ли в вашей коллекции data элементы или нет.

По этой причине, я не думаю, что линии

return data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime;

является реальной проблемой, так как исключение с сообщением Sequence contains no elements создается, когда какая-либо операция выполняется в пустой коллекции. Поскольку есть элементы в пределах data (а затем data.Any() возвращает true), исключение касается другой строки в вашем коде.

Чтобы получить полную стек исключений и лучше понять, какая строка вызывает ошибку, вы должны добавить больше регистрации в свое приложение.

0

Попробуйте метод FirstOrDefault().

var lastUpdated = DateTime.MinValue; 
var first = data.OrderByDescending(d => d.UpdatedTime).FirstOrDefault(); 
if (first != null) 
{ 
    lastUpdated = first.UpdatedTime; 
} 
0

Я не знаю, что такое определение MyObject.Но я предполагаю, что поле UpdatedTime - DateTime?. Если да, то это произойдет в последний метод, когда:

lastUpdated = data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime; 
return String.Format("<tr style=\"display:none\"><td><div Id='MetaInfo' data-lastUpdated='{0}' /></td></tr>", lastUpdated.Ticks); 

Если UpdatedTime является null и lastUpdated.Ticks бросок NullReferenceException.

Смежные вопросы