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.
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:
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).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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With