让我们从这些方法的实现开始:
FirstOrDefault
Source(它有两个没有谓词的重载)
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(它有两个没有谓词的重载)
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);
}
他们都打电话给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);
}
与Provider 通话比较
同步
return source.Provider.Execute<TSource>(Expression.Call(...))
异步
return provider.ExecuteAsync<TResult>(Expression.Call(...))
如您所见,这两个调用之间的区别在于您与数据源的通信方式:
- 如果您以同步方式执行此操作,则在底层网络驱动程序执行请求的 I/O 操作时,您的调用线程将被阻塞并保持空闲。
- 如果您以异步方式执行此操作,则在将工作分派给底层网络驱动程序以非阻塞方式执行请求的 I/O 操作后,您的调用线程将被释放。因此,您的调用者线程可以在等待驱动程序通知调度程序请求的操作已运行完成时执行其他代码。