3
votes

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?

3
If you wanted to do this to an arbitrary IEnumerable in code, I'd suggest a custom extension method; but given that these items are from a SQL server, if you want to do it without always iterating all records, you need to do it on the database server. I'd consider it unlikely that there's a set of LINQ methods that could be automatically translated, so a sproc would be my suggestion.AakashM

3 Answers

1
votes

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.

1
votes

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();
1
votes

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.