I am using Entity Framework Core for my ASP.NET Core WebApi project.
I came across these two methods while using Linq, I am curious to find the difference between FirstOrDefaultAsync
vs FirstOrDefault
?

- 17,736
- 16
- 35
- 75

- 1
- 1
- 1
-
Please read https://learn.microsoft.com/en-gb/dotnet/csharp/programming-guide/concepts/async/ – Pablo Recalde Oct 26 '20 at 07:13
-
1Does this answer your question? [How and when to use ‘async’ and ‘await’](https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await) – SomeBody Oct 26 '20 at 07:19
-
I think you shouw first learn how async , await works – TAHA SULTAN TEMURI Oct 26 '20 at 07:23
2 Answers
Excellent medium post here: https://medium.com/@deep_blue_day/long-story-short-async-await-best-practices-in-net-1f39d7d84050
Summarized:
There are basically two scenarios where Async/Await is the right solution.
I/O-bound work: Your code will be waiting for something, such as data from a database, reading a file, a call to a web service. In this case you should use Async/Await, but not use the Task Parallel Library.
CPU-bound work: Your code will be performing a complex computation. In this case, you should use Async/Await but spawn the work off on another thread using Task.Run. You may also consider using the Task Parallel Library.
Your case falls down on the I/O-bound work.
Why is there a non-async version then? That is to support legacy code and/or code where using asynchronous methods is not allowed, due to execive refactoring. (one async method, means all the call stack should be async).

- 25,191
- 4
- 32
- 61
Let's start with the implementation of these methods:
FirstOrDefault
Source (It has two overloads w/o Predicate)
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source)
{
if (source == null)
throw Error.ArgumentNull("source");
return source.Provider.Execute<TSource>(
Expression.Call(
null,
GetMethodInfo(Queryable.FirstOrDefault, source),
new Expression[] { source.Expression }
));
}
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
if (source == null)
throw Error.ArgumentNull("source");
if (predicate == null)
throw Error.ArgumentNull("predicate");
return source.Provider.Execute<TSource>(
Expression.Call(
null,
GetMethodInfo(Queryable.FirstOrDefault, source, predicate),
new Expression[] { source.Expression, Expression.Quote(predicate) }
));
}
FirstOrDefaultAsync
Source (It has two overloads w/o Predicate)
public static Task<TSource> FirstOrDefaultAsync<TSource>(
[NotNull] this IQueryable<TSource> source,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
return ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.FirstOrDefaultWithoutPredicate, source, cancellationToken);
}
public static Task<TSource> FirstOrDefaultAsync<TSource>(
[NotNull] this IQueryable<TSource> source,
[NotNull] Expression<Func<TSource, bool>> predicate,
CancellationToken cancellationToken = default)
{
Check.NotNull(source, nameof(source));
Check.NotNull(predicate, nameof(predicate));
return ExecuteAsync<TSource, Task<TSource>>(QueryableMethods.FirstOrDefaultWithPredicate, source, predicate, cancellationToken);
}
They are both calling the following ExecuteAsync overload:
private static TResult ExecuteAsync<TSource, TResult>(
MethodInfo operatorMethodInfo,
IQueryable<TSource> source,
Expression expression,
CancellationToken cancellationToken = default)
{
if (source.Provider is IAsyncQueryProvider provider)
{
if (operatorMethodInfo.IsGenericMethod)
{
operatorMethodInfo
= operatorMethodInfo.GetGenericArguments().Length == 2
? operatorMethodInfo.MakeGenericMethod(typeof(TSource), typeof(TResult).GetGenericArguments().Single())
: operatorMethodInfo.MakeGenericMethod(typeof(TSource));
}
return provider.ExecuteAsync<TResult>(
Expression.Call(
instance: null,
method: operatorMethodInfo,
arguments: expression == null
? new[] { source.Expression }
: new[] { source.Expression, expression }),
cancellationToken);
}
throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
}
Compare the to Provider
call
Sync
return source.Provider.Execute<TSource>(Expression.Call(...))
Async
return provider.ExecuteAsync<TResult>(Expression.Call(...))
As you can see the difference between these two calls is that how you communicate with the data source:
- If you are doing it in a synchronous fashion then your calling thread is blocked and remains idle while the underlying network driver executes the requested I/O operation.
- If you are doing it in an asynchronous fashion then your calling thread is freed up after it has dispatched the work to the underlying network driver to perform the requested I/O operation in a non-blocking way. So your caller thread can execute other code while its waiting for the driver to notify the scheduler that the requested operation has run to completion.

- 17,736
- 16
- 35
- 75