I've seen a few questions about lambdas and exceptions, but I still don't understand the behavior I'm seeing in my code.
class MyObj
{
public object Prop1 { get; set; }
}
public void Get()
{
try
{
IEnumerable<MyObj> collection = new List<MyObj>() { new MyObj() };
collection = collection.Where(x =>
{
return x.Prop1.ToString() == "some value";
});
}
catch (Exception ex)
{
throw new UriFormatException($"Invalid filter. Error details: {ex.Message}");
}
}
Since Prop1
is null, an "Object reference not set to an instance of an object" exception is thrown. However, the code doesn't enter the catch
block. If I add some more code that attempts to use collection
in the try
block, the catch
block is entered when the exception is encountered in the lambda. E.g.
foreach (var c in collection)
{
Console.WriteLine(c);
}
I'd like the catch block to be entered if there's any exception when executing the lambda. I've tried writing a named function to call from the lambda, that doesn't work either. What am I missing?
LINQ operators like Where
use something called "deferred execution." This means that when you call them, they don't actually execute the lambda you give them: they just return a new query that keeps track of the lambda and the source. Then, when you try executing that query, it will start evaluating the lambda.
This can be beneficial because if you end up putting something like a .First()
or a .Take(10)
at the end of the query, it can avoid running the delegate on every single item in your collection. But it can be problematic for various reasons: if the same query gets executed multiple times, it'll repeat a lot of work; if the values captured in the lambda change before execution, it can meaningfully change the behavior of the query.
So if you want to catch exceptions thrown in your query's delegates, you'll either need to put a try/catch around your foreach loop or put something like a .ToList()
in there to force immediate execution.
try
{
var collection = new List<MyObj>() { new MyObj() }
.Where(x =>x.Prop1.ToString() == "some value")
.ToList();
}
catch (Exception ex)
{
throw new UriFormatException($"Invalid filter. Error details: {ex.Message}");
}