Linq statement crashing (no exception) on 2nd method call instantiate object - c#

I have a method which instantiates an object, some of the properties of this object are arrays where i use linq to fetch the data.
private static GrowthYieldStructure CreateGrowthYieldStructure(int timberType, IEnumerable<Tree> trees)
{
var trees1 = trees;
return new GrowthYieldStructure
{
TimberType = timberType,
CurrentDbhList = trees1.Select(x => x.DBH).ToArray(),
CurrentSpeciesList = trees1.Select(x => x.SpeciesNumber).ToArray(),
CurrentTpaList = trees1.Select(x => x.TPA).ToArray(),
CurrentTreeListLength = trees1.Count()
};
}
The first time I call this method it works fine.
The second time it will fail with no exception on the second select statement.
-no matter which value its attempting to select
For instance, trees1.Select(x => x.DBH).ToArray() works, trees1.Select(x => x.SpeciesNumber).ToArray() crashes.
(I've tried switching the fetching order / making local variable copies / I've checked that values exist and they do, nothing out of the ordinary / using try/catch (no exp caught))
Edit:
I made more local variables to store the IEnumerable; still fails
If I only have one select statement it will run fine...
--
Edit2: (calling code - could be off going from memory)
stands,plots,trees are all IEnumerable (T being Stand,Plot,Tree)
foreach (var plot in plots.Where(x => x.StandID.Equals(stands.ID))) {
var plot1 = plot;
var treeList = trees.Where(x => x.PlotID.Equals(plot1.ID));
var growthYieldStructure = CreateGrowthYieldStructure(stands.TimberType, treeList); }
Edit3:
Finally saw this error:
A first chance exception of type 'System.AccessViolationException' occurred in Unknown Module.
Then finally realized my error-
It was actually the code using after the object creation.
I was using arrays to send to a external library since arrays are reference types this worked out they way I suspected.
But since I was not copying the arrays and instead creating a new local variable which would have the same memory reference.
This caused the next object init to fail since it wanted to write in the same memory loc..
I just changed the object to use IEnumerable this way i can have the array reference once.
Sorry for the confusion.
Any thoughts to why it is crashing?

It might be do to the IEnumerable being an IQueryable and it is trying to enumerate more than once.
Try changing the line:
var trees1 = trees;
to
var trees1 = trees.ToList();
that will force the enumeration and trees1 will be a List instead of the possible IQueryable

Related

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.
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;
Try FirstOrDefault() method.
var lastUpdated = DateTime.MinValue;
var first = data.OrderByDescending(d => d.UpdatedTime).FirstOrDefault();
if (first != null)
{
lastUpdated = first.UpdatedTime;
}
I don't know what MyObject definition is like. But I am guessing the
UpdatedTime field is DateTime?. If so, it happen at last method, when:
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);
Where the UpdatedTime is null, and lastUpdated.Ticks throw NullReferenceException.
Your code checks first if any element of data collection satisfies a predicate with data.Any(). Since there's no predicate, this call is equivalent to tell you if your data collection has elements or not.
For this reason, I don't think that the line
return data.OrderByDescending(d => d.UpdatedTime).First().UpdatedTime;
is the real problem, because an exception with the message Sequence contains no elements is thrown when some operation is performed in an empty collection. Since there're elements within data (and then data.Any() returns true), the exception is about another line in your code.
You should add more logging to your application, in order to get the full stacktrace of the exception and understand better which line is causing the error.

Why would a C# method returning IEnumerable work when it returns a List but not when it yields?

I have a method that generates a set of options for a floating drop-down menu. The method is of type IEnumerable<FloatMenuOption>.
When I return the values as a List, it works fine. But when I yield them one by one, every item runs the useAct lambda for the last one yielded, even though they all have correct labels.
Can anyone explain why this might happen? Why would returning a List instead of yielding the items one by one matter?
public override IEnumerable<FloatMenuOption> GetFloatMenuChoicesFor(Pawn myPawn)
{
List<FloatMenuOption> options = new List<FloatMenuOption>();
foreach ( Communicable commTarget in GetCommTargets() )
{
var localCommTarget = commTarget;
System.Action useAct = () =>
{
Job openJob = new Job();
openJob.commTarget = localCommTarget;
myPawn.MindHumanoid.TakeOrderedJob(openJob);
};
options.Add( new FloatMenuOption(localCommTarget.GetLabel(), useAct) );
}
return options;
//Simply commenting out the above line and uncommenting the two below causes the error
// foreach (var opt in options)
// yield return opt;
}
In either case, the options come out with correct labels (from localCommTarget.GetLabel()). However, if yielded, they all do the useAct lambda configured for the last item in the list, while if returned as a list they each do their own useAct lambda.
Why?
Take a look at Captured variable in a loop in C#
I believe if you change your for loop to:
foreach (var opt in options)
{
var capturedOpt = opt;
yield return capturedOpt;
}
you'll be golden.
In your implementation, if your code yields the results instead of adding them to the list, only the last assignment to localCommTarget is used. The reason for this is the fact that the execution of the lambda expression is deferred to the actual access to the return. In other words, the order of execution is different for the different return strategies.

Database.Query return type should be IEnumerable<dynamic>

I need help to understand this thing. Why in the following code the variables "ejes" and "habilidades" are resolved as "dynamic" and the third as IEnumerable<dynamic>. This is affecting the code that runs next, an exception appears when I try to invoke the extension method "Count()" because "ejes" and "habilidades" are not IEnumerable. They are the result of the same method "Database.Query".
Here is the snippet:
var db = Database.Open("froned");
db.Execute("begin transaction");
try
{
var asignacion = db.QuerySingle("select * from asignacion_avanza where id_asignacion = #0", id_asignacion);
var ejes = db.Query(String.Format(#"
select id_eje
from asignatura_eje_nivel
where id_nivel = {0}
and id_asignatura = {1}",
asignacion.id_nivel,
asignacion.id_asignatura));
var habilidades = db.Query(String.Format(#"
select id_habilidad
from asignatura_habilidad_nivel
where id_nivel = {0}
and id_asignatura = {1}",
asignacion.id_nivel,
asignacion.id_asignatura));
var dificultades = db.Query("select id_dificultad from dificultad");
var c_dif = dificultades.Count();
var c_eje = ejes.Count();
var c_habilidades = habilidades.Count();
I put an Image of the debugger to show the runtime type of the variables.
asignacion.id_nivel and asignacion.id_asignatura are dynamic types.
When you pass a dynamic type into any method as an argument, the return type of the method becomes dynamic instead of whatever MSDN says it is. You cannot use extension methods on dynamic types. Count() is an extension method on Enumerable. That's why you get the exception.
There are two ways to solve the problem and revert the return type to Enumerable. The first is to define it explicitly:
IEnumerable<dynamic> data = db.Query(someSQL, parameterValue);
and the other is to cast the parameter to a non-dynamic type:
var data = db.Query(someSQL, (string)parameterValue);
And as Knox points out, you must use parameters rather than string.Format.
The difference is debugger is due to dificultades has already evaluated
(var c_dif = dificultades.Count();).
Other two variables is not evaluated yet (linq deferred).
So debugger knows more about dificultades.
Are you sure that habilidades and the other one have data? I think it might be that db.Query is returning a null, meaning no rows returned, and then habilidades.Count can't execute because null doesn't have an object to execute against. One way to test this outside the debugger is to do a db.QueryValue( "Select Count(*) from ...") and see if you're getting zero rows returned.
By the way, the database functions in C# build in the variable handling in a slightly shorter way than you have. You can write
var ejes = db.Query( #"
Select *
From asign
Where id_nivel = #0",
asignacion.id_nivel );
This technique is called parameterized SQL. I'm only showing one parameter, but you will need more. The #0, #1, #2, and so forth get replaced just like in a String.Format.

Parallel.ForEach and IGrouping source item issue

I am trying to parallelize a query with a groupby statement in it. The query is similar to
var colletionByWeek = (
from item in objectCollection
group item by item.WeekStartDate into weekGroups
select weekGroups
).ToList();
If I use Parallel.ForEach with shared variable like below, it works fine. But I don't want to use shared variables in parallel query.
var pSummary=new List<object>();
Parallel.ForEach(colletionByWeek, week =>
{
pSummary.Add(new object()
{
p1 = week.First().someprop,
p2= week.key,
.....
});
}
);
So, I have changed the above parallel statement to use local variables. But the compiler complains about the source type <IEnumerable<IGrouping<DateTime, object>> can not be converted into System.Collections.Concurrent.OrderablePartitioner<IEnumerable<IGrouping<DateTime, object>>.
Am I giving a wrong source type? or is this type IGouping type handled differently? Any help would be appreciated. Thanks!
Parallel.ForEach<IEnumerable<IGrouping<DateTime, object>>, IEnumerable<object>>
(spotColletionByWeek,
() => new List<object>(),
(week, loop, summary) =>
{
summary.Add(new object()
{
p1 = week.First().someprop,
p2= week.key,
.....
});
return new List<object>();
},
(finalResult) => pSummary.AddRange(finalResult)
);
The type parameter TSource is the element type, not the collection type. And the second type parameter represents the local storage type, so it should be List<T>, if you want to Add() to it. This should work:
Parallel.ForEach<IGrouping<DateTime, object>, List<object>>
That's assuming you don't actually have objects there, but some specific type.
Although explicit type parameters are not even necessary here. The compiler should be able to infer them.
But there are other problems in the code:
you shouldn't return new List from the main delegate, but summary
the delegate that processes finalResult might be executed concurrently on multiple threads, so you should use locks or a concurrent collection there.
I'm going to skip the 'Are you sure you even need to optimize this' stage, and assume you have a performance issue which you hope to solve by parallelizing.
First of all, you're not doing yourself any favors trying to use Parallel.Foreach<> for this task. I'm pretty sure you will get a readable and more optimal result using PLINQ:
var random = new Random();
var weeks = new List<Week>();
for (int i=0; i<1000000; i++)
{
weeks.Add(
new Week {
WeekStartDate = DateTime.Now.Date.AddDays(7 * random.Next(0, 100))
});
}
var parallelCollectionByWeek =
(from item in weeks.AsParallel()
group item by item.WeekStartDate into weekGroups
select new
{
p1 = weekGroups.First().WeekStartDate,
p2 = weekGroups.Key,
}).ToList();
It's worth noting that there is some overhead associated with parallelizing the GroupBy operator, so the benefit will be marginal at best. (Some crude benchmarks hint at a 10-20% speed up)
Apart from that, the reason you're getting a compile error is because the first Type parameter is supposed to be an IGrouping<DateTime, object> and not an IE<IG<..,..>>.

Convert anonymous type to array or arraylist. Can it be done

Trying to retrieve data using linq from a database. I would like to use anonymous types and convert to an Ilist, Array, ArrayList or Collection. The data is used in a third party object that accepts Ilist, arraylist or collections.
I can't seem to get this to work. I get the following error, "Sequence operators not supported for type 'System.String'"
using (var db = new dbDataContext())
{
var query = from e in db.people
select new
{
Count = e.LastName.Count()
};
Array test;
test = query.ToArray();
}
It's got nothing to do with converting the results to array lists, or even anonymous types. Here's another version of your code which will fail:
using (var db = new dbDataContext())
{
var query = db.people.Select(x => x.LastName.Count());
foreach (int x in query)
{
Console.WriteLine(x);
}
}
That will still fail in the same way - because it's the translation of this bit:
x => x.LastName.Count()
into SQL which is causing problems.
Change it to:
x => x.LastName.Length
and I suspect you'll find it works. Note that this isn't really a C# issue - it's just LINQ to SQL's translation abilities.
I would suggest that you don't use an anonymous type here though - it's pointless. Maybe this isn't your complete code, but in general if you find yourself creating an anonymous type with a single member, ask yourself if it's really doing you any good.
The ArrayList class has a constructor that accepts ICollection.
You should be able to feed it a List version of your LINQ result.
using (var db = new dbDataContext()) {
var query = from e in db.people
select new { Count = e.LastName.Count() };
ArrayList list = new ArrayList(query.ToList());
}
I don't have Visual Studio here (I'm on my Mac), but it might be of help.
(ToArray should suffice as well)
You might need to replace your Count() by Length.

Categories