comecme
comecme

Reputation: 6386

LINQ FirstOrDefault with predicate, if not found just FirstOrDefault

I'm trying to search in an IEnumerable for a specific record (using a predicate). If that record doesn't exist, I want to return the first record. If that also doesn't exist, I just want null.

I'm currently using

var category = categories.FirstOrDefault(
    c => c.Category == "C") ??
    category.FirstOrDefault();

ReSharper gives me a warning (Possible multiple enumeration of IEnumerable). The warning tells me my sql statement might be executed twice. Once to try and find a "C" category, and once more to get the first record. The warning will go away if I first convert categories to a List by using categories.ToList(). But if categories contains a lot of records, that could be slow.

Is there a more elegant way to do this? Or should I just ignore the warning?

Upvotes: 3

Views: 4597

Answers (3)

MarkO
MarkO

Reputation: 2233

The ReSharper warning is more a message saying "Think about what you are doing", not a "You are doing it wrong".

Your solution is not that bad, but it all depends on the situation.

In production, how often will the category not exist? Is it a rare case, then leave your code as is. Another thing to consider is the frequency in which this code is executed. Is it once a day, or ten times per second? Is it more like the first? Leave it as is.

Otherwise, micro optimization can be of benefit. A solution like Seyana might work or you could for instance rewrite the queries to be a union(), with a Take(1), so only one query will be send to SQL Server; but that doesn't mean both queries won't be executed by the database engine.

Profiling will give your answer to which solution is the fastest or uses the least amount of resources.

Upvotes: 1

Omri Aharon
Omri Aharon

Reputation: 17064

If you have a column in your categories table that is sequential, you could do something like this:

var category = categories.Where(c => c.SomeSequentialId == 1 || c.Category == "C")
.OrderByDescending(c => c.SomeSequentialId)
.FirstOrDefault();

Upvotes: 1

Dan
Dan

Reputation: 10538

If you are really worried about performance, FirstOrDefault is O(n). Your second query is only going to execute if the first is null. You could try using .Any(predicate). First() lends itself to try { } catch which might be better for your situation.

You don't really need to worry about performance optimization until it becomes an issue, though.

Upvotes: 1

Related Questions