c# Linq to Objects - FirstOrDefault performance - c#

We're trying to optimize some of our methods. We use Redgate's Performance profiler to find some performance leaks.
Our tool uses Linq to objects in several methods. But we have noticed that a FirstOrDefault takes very long on collections with +/- 1000 objects.
The profiler also alerts that the query is very slow. I've added images with the profiler results.
It's not possible to add the collection to a database and then query the database.
Any recommendations?
Thanks !
private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant)
{
SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null;
if (saldoPrivatiefKlantVerdeelsleutel != null)
{
try
{
parentSaldoPrivatiefKlantVerdeelsleutel = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection
.FirstOrDefault(s => (boeking == null || (s.SaldoVerdeelsleutel != null &&
(s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID)))
&& s.PrivatiefKlant.ID == privatiefKlant);
}
catch (Exception ex)
{ }
}
return parentSaldoPrivatiefKlantVerdeelsleutel;
}
Image :
Profile report

You should be able to speed it up by rewriting it as
saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection
.Where(s => (boeking == null || (s.SaldoVerdeelsleutel != null &&
(s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant)
.FirstOrDefault()
See Why is LINQ .Where(predicate).First() faster than .First(predicate)? for why this is faster.

I'll say that this
boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID
could be the culprit. Try caching it outside, like:
var id = boeking != null ? boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID : 0;
and use the id inside the query.
(I'm doing a supposition: one of the properties of that long chain do something "not too much smart" and is in fact quite slow)

FirstOrDefault do standard linear search over source collection and returns first element that matches a predicate. It's O(n), so it's not surprising that it takes more time on bigger collections.
You can try following, but the gain won't be huge, because it's still O(n).
private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant)
{
SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null;
if (saldoPrivatiefKlantVerdeelsleutel != null)
{
try
{
var query = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode
.SaldoPrivatiefKlantVerdeelsleutelCollection
.Where(s => s.PrivatiefKlant.ID == privatiefKlant);
if(boeking != null)
{
var gebouwVerdeelSleutelId = boeking.SaldoGebouwRekeningVerdeling
.SaldoGebouwRekening
.SaldoVerdeelsleutel
.GebouwVerdeelSleutel
.ID;
query = query.Where(s => s.SaldoVerdeelsleutel != null &&
s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == gebouwVerdeelSleutelId);
}
parentSaldoPrivatiefKlantVerdeelsleutel = query.FirstOrDefault();
}
catch (Exception ex)
{ }
}
return parentSaldoPrivatiefKlantVerdeelsleutel;
}
It will make better, because boeking != null check will be done only once, not on every source collection element. And because nested Where calls are combined they will not cause performance penalty.

You can try write it like a simple code. LINQ is using delegates this is why there is a little perfomance hit.
try
{
parentSaldoPrivatiefKlantVerdeelsleutel = null;
foreach (var s in saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection)
{
if ((boeking == null || (s.SaldoVerdeelsleutel != null && (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant)
{
parentSaldoPrivatiefKlantVerdeelsleutel = s;
break;
}
}
}
catch (Exception ex)
{ }

Related

How can I use a lambda expression in a while loop condition?

while(list.next() != null && list.next().asInteger() != 6)
{
...
}
Is it possible to use an inline lambda function here to avoid calling list.next() twice? If next() actually removed the element from the list, this would be essential not just convenient.
YourType foo;
while ((foo = list.next()) != null && foo.asInteger() != 6)
{
}
You can use for instead of while:
for (var item = list.next(); item != null && item.asInteger() !=6; item = list.next()) {
...
}
You can assign variables in while loops. It would be easier to write it like so.
WhateverNextReturns nextListItem;
while ((nextListItem = list.next()) != null) && nextListItem.asInteger() != 6)
// Do some stuff
Or better yet...
WhateverNextReturns nextListItem;
while(true)
{
nextListItem = list.next();
if (nextListItem == null || nextListItem == 6)
break;
//Do some stuff
}
I think using a lambda would make this more complex than what it has to be.
Here's another example in this StackOverflow Answer of someone assigning a variable in a while expression
It is possible to do this with an inline lambda, but C# does not like it:
while (((Func<YourType, bool>)(n => n != null && n.asInteger() != 6))(list.next()))
{
...
}
is this list an IEnumerable object? You can always use
list.Where(t!=null && t=>t.asInteger()==6).ToList().ForEach(t=>{
//do anything you want with t here.
});
Tell me if I got the question wrong.

MongoDB Select with QueryBuilder

i'm trying to select values from my database, but currently i'm unable to to it and although i know its the fact that the method doesnt except the QueryBuilder class as a parameter, i dont know what to do about it. I only found solutions for querys with one parameter or all parameters are not null. In my case i've a List with ID, and 4 parameters which dont have to be passed to the function so they could be null.
My current code looks like this.
collection = db.GetCollection<Datapoint>("test");
var query = new QueryBuilder<Datapoint>();
var queryattributes = new List<IMongoQuery>();
var ids = new List<IMongoQuery>();
// Add all Attributes if there
if (fromdate != null)
{
BsonDateTime from = BsonDateTime.Create(fromdate);
queryattributes.Add(Query.GTE("UTCTimestamp", from));
}
if (to != null)
{
BsonDateTime bto = BsonDateTime.Create(to);
queryattributes.Add(Query.LTE("UTCTimestamp", bto));
}
if (evId != null)
{
queryattributes.Add(Query.EQ("EvId", evId));
}
if (evType != null)
{
queryattributes.Add(Query.EQ("EvType", evType));
}
// Add all ID's
Parallel.ForEach(idList, data =>
{
lock (query)
{
ids.Add(Query.EQ("CId", data));
}
});
// But everything in the Query
query.Or(ids);
// Add Queryattributes if there
if (queryattributes.Count > 0)
{
query.And(queryattributes);
}
var result = collection.FindAs<Datapoint>(query);
I'm trying to do it without Linq, since i found countless of test, which say that linq is much much slower, and since i want to run it as an Databasetest, i kinda need the performace to execute alot of querys.
The Linq query looks like this
var query2 =
from e in collection.AsQueryable<Datapoint>()
where idList.Contains(e.CId)
&& (evtId == null || e.EvId == evId)
&& (evType == null || e.EvType == evType.Value)
&& (fromdate == null || Query.GTE("UtcTimestamp", BsonDateTime.Create(fromdate)).Inject())
&& (to == null || Query.LT("UtcTimestamp", BsonDateTime.Create(to)).Inject())
select e;
The QueryBuilder doesn't save a query inside it. You use it to generate a IMongoQuery and then use that query to actually query the database.
It seems the end of your code should look like this;
// But everything in the Query
IMongoQuery mongoQuery = query.Or(ids);
// Add Queryattributes if there
if (queryattributes.Count > 0)
{
mongoQuery = query.And(queryattributes);
}
var result = collection.FindAs<Datapoint>(mongoQuery);

Variable Declaration inside an IF C#

take a look at the following code :
if( down == null || down.GetFace() != Face.None || down.GetFace() != Face.Partial )
{
// I called GetFace() two times
// How can i avoid to call it two times, and still putting inside that if
}
Thank you!
To be more maintainable and expressive first separate the null check as exceptional case
then get the result to a variable and check it.
if(down == null)
// Some exceptional case .. return or throw exception
var result = down.GetFace();
if(result == Face.None || result != Face.Parial)
// Do your code here
Refactor to a method:
private bool IsFaceNoneOrPartial(Down down)
{
var face = down.GetFace();
return face != Face.None || face != Face.Partial;
}
// Your code is now:
if( down == null || IsFaceNoneOrPartial(down))
{
}

Nested 'if'-'else' statements

I have code that's very messy with the if - else if checks it is doing. The amount of branching and nested branching is quite big (over 20 if - else if and nested too). It's making my code harder to read and will probably be a performance hog. My application checks for a lot of conditions it gets from the user and so the application must check all the time for different situations, for example:
If the textbox text is not 0, continue with the next...
if ((StartInt != 0) && (EndInt != 0))
{
And then here it checks to see whether the user have choosen dates:
if ((datePickerStart.SelectedDate == null) || (datePickerEnd.SelectedDate == null))
{
MessageBox.Show("Please Choose Dates");
}
Here, if the datepickers aren't null it continues with code...
else if ((datePickerStart.SelectedDate != null) && (datePickerEnd.SelectedDate != null))
{
// CONDITIONS FOR SAME STARTING DAY AND ENDING DAY.
if (datePickerStart.SelectedDate == datePickerEnd.SelectedDate)
{
if (index1 == index2)
{
if (StartInt == EndInt)
{
if (radioButton1.IsChecked == true)
{
printTime3();
}
else
{
printTime();
}
}
This is just a small part of the checks being made. Some of them are functionals and some are for input validation stuff.
Is there any way to make it more readable and less of a performance hog?
It's not that of a performance hog. A great blog post on how to fix those common problems is Flattening Arrow Code.
I see here some mix in validation. Try to move one fields from others, and validate them separately, something like this:
if (StartInt == 0 || EndInt == 0)
{
MessageBox.Show("Please Choose Ints");
return;
}
if (datePickerStart.SelectedDate == null || datePickerEnd.SelectedDate == null)
{
MessageBox.Show("Please Choose Dates");
return;
}
In this approach you'll always say to the user what he did wrong, and your code is much simpler.
More information from Jeff's blog
One way is to refactor by encapsulating complex conditions in the following way:
public bool DateRangeSpecified
{
get
{
return (datePickerStart.SelectedDate != null)
&&
(datePickerEnd.SelectedDate != null)
&& StartInt != 0 && EndInt != 0;
}
}
and then using these "condition facade" properties
Some slight refactoring makes it easier to read to my eyes. I removed extraneous brackets and consolidated multiple IF statements that are really just AND logic.
if (StartInt == 0 || EndInt == 0)
return;
if (datePickerStart.SelectedDate == null || datePickerEnd.SelectedDate == null)
{
MessageBox.Show("Please Choose Dates");
return;
}
if (datePickerStart.SelectedDate != null
&& datePickerEnd.SelectedDate != null
&& datePickerStart.SelectedDate == datePickerEnd.SelectedDate
&& index1 == index2
&& StartInt == EndInt)
{
if (radioButton1.IsChecked == true)
printTime3();
else
printTime();
}
You can define your own predicates or generic functions with meaningful names and encapsulate you logic into those.
Here is a code example for some predicates:
public Predicate<DateTime> CheckIfThisYear = a => a.Year == DateTime.Now.Year;
public Func<DateTime, int, bool> CheckIfWithinLastNDays = (a, b) => (DateTime.Now - a).Days < b;
Now you can easily write in your code
if (CheckIfThisYear(offer) && CheckIfWithinLastNDays(paymentdate,30)) ProcessOrder();
Consider using generic delegates, like Func<> and Delegate<> for writing small blocks of your conditions using lambda-expressions -- it will both save the space and makes your code much more human-readable.
Use the return statement to stop the execution of a block.
For instance,
void Test()
{
if (StartInt==0 || EndInt==0)
{
return;
}
if (datePickerStart.SelectedDate == null || datePickerEnd.SelectedDate == null)
{
MessageBox.Show("Please Choose Dates");
return;
}
}

Null check ObservableCollection before querying it

Is this code good enough:
if (MyCollection.Where(p => p.SomeID == idstring).Any())
{
selectedval = MyCollection.Where(p => p.SomeID == idstring).FirstOrDefault().MyField;
}
My doubt is about the fact that I make the same query twice: first for null check and then to actually get data.
Maybe there is a better way to do this type of things?
Yes.
var item = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if (item != null)
selectval = item.MyField;
This avoids double querying the collection, which will certainly make a difference in big collections or if your collection performs a DB query.
There is. You can use the FirstOrDefault method which takes a predicate and returns null if the item is not found.
var result = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if( result != null )
{
// item was found
}

Categories

Resources