Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error with LINQ: Sequence contains no elements

Occasionally, my users experience the issue where in the log files I can see this exception is thrown (Sequence contains no elements)

I search around and can see this exception happens when you try to access or use aggregate on an empty list.

I searched through the code around this exception (too bad no stacktrace was logged), and the only "potential" culprit is the below lines (that use either Fist(), Last(), Single() or any Aggregate). However I can't understand why and not able reproduce on my local. Please help to advise.

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

Here, data is a List<MyObject> and MyObject has DateTime property called UpdatedTime

===== More surrounding code =====

This is where I got the unhandled exception in the log. GetRecentUpdates method has its own try catch block, so ruled out.

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);
}

The javascript calls GetUpdatedTime every 10s. Ussually everything goes fine, but every once in awhile, this exception is thrown. Once it's thrown, it keeps being thrown every 10s until the user refreshes the page.

like image 897
harry Avatar asked Sep 16 '25 04:09

harry


1 Answers

Update:

Another version after some investigate: as you've said, your code is running in multhithreading environment, and the data object could be accessed by two or more threads. As it's a reference type variable, the reference of it can be modified. So, consider such situation:

First thread enters the Build method and checking the condition:

if (data.Any())

and the data isn't empty at this moment, so it enters the true block. Right exactly in this time another one thread enters the Build method, but on that moment the data variable is empty, and all reference for it points to empty List. But the first one thread already into the true block:

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

And it fails with you exception. And now good news: you can fix it in many ways:

  • First of all, check the creating data logic. May be, it is a static or shared variable, or object it being populated from, is a static or shared variable, and you have race condition for this resource. You may change the logic of it's creation or wrap it into a some synchronizing primitive so the only one thread can Build at the same time (but this can affect the performance of your program).
  • Change the logic of GetRecentRequests - can't say for sure, but I think that the situation is something like this: commonRequests are empty all the time, and for the first thread dictionary got some data, but has no data for second thread, and data object is being overridden and is empty. Way to debug it: add Barrier primitive to your program during test run, and wait for 10-15 threads being awaiting for the barrier. After that they'll start simultaneously build your data and error will happen with high probability (do not insert breakpoints - they'll sychronize your threads).
  • Make a local copy of data object, something like this:

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

Hope this helps.


Your code is checking, if some data is available, and after that filters the data by date. As you are using the LINQ extension methods, I think that data is an IEnumerable object, not the List, so, when you are calling Any() method, it's being enumerated, and after that, you are calling the First() method, which is enumerating it too.

So, if your data is being a result of some yeild return method, it's being enumerated once, and on second time there is no data there, and sequence is empty.

Consider to change your code to work with data as a List or Array, or use the FirstOrDefault method to have a null object if there is no data, like this:

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

or

var firstElement = data.OrderByDescending(d => d.UpdatedTime).FirstOrDefault();
return firstElement != null ? firstElement.UpdatedTime : DateTime.MinValue;
like image 169
VMAtm Avatar answered Sep 17 '25 17:09

VMAtm