Skip to content

Reasons for Redundant AsQueryable() Calls When Upgrading to .NET 6

TLDR

  • Redundant AsQueryable() calls found in .NET 6 projects are usually a result of upgrading from older project versions.
  • In older versions of EF Core (prior to 5), DbSet<TEntity> implemented both IQueryable<TEntity> and IAsyncEnumerable<TEntity>, causing ambiguity for the compiler when processing LINQ extension methods.
  • Calling AsQueryable() was necessary to explicitly specify the type as IQueryable<TEntity> to avoid extension method conflicts introduced by System.Linq.Async.
  • EF Core 6 removed the direct implementation of IAsyncEnumerable<TEntity> from DbSet<TEntity>, resolving this conflict; therefore, it is safe to remove redundant AsQueryable() calls in a .NET 6 environment.

Problem Context and Root Cause Analysis

In projects upgraded to .NET 6, developers often find code littered with DbContext.Table.AsQueryable(). Since DbSet<TEntity> already implements IQueryable<TEntity>, there is theoretically no need for an additional AsQueryable() call.

This phenomenon primarily occurs in projects that reference the System.Linq.Async package and were originally based on EF Core 5 or earlier.

Technical Details of Extension Method Conflicts

In versions prior to EF Core 5, the interface implementation of DbSet<TEntity> was as follows:

csharp
public abstract class DbSet<TEntity> : 
    Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>, 
    System.Collections.Generic.IAsyncEnumerable<TEntity>, 
    System.Collections.Generic.IEnumerable<TEntity>, 
    System.ComponentModel.IListSource, 
    System.Linq.IQueryable<TEntity> where TEntity : class

When a project imports System.Linq.Async:

  • The Queryable class defines extension methods for IQueryable<TEntity> (e.g., Where, Select).
  • The AsyncEnumerable class defines extension methods for IAsyncEnumerable<TSource> (e.g., Where, Select).

Because DbSet<TEntity> implements both interfaces, the compiler cannot determine which extension method the developer intends to call, resulting in a compilation error. To force the use of IQueryable extension methods, developers had to explicitly call AsQueryable().

Improvements in EF Core 6

In EF Core 6 and later versions, the interface implementation of DbSet<TEntity> has been adjusted to:

csharp
public abstract class DbSet<TEntity> : 
    Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<IServiceProvider>, 
    System.Collections.Generic.IEnumerable<TEntity>, 
    System.ComponentModel.IListSource, 
    System.Linq.IQueryable<TEntity> where TEntity : class

Conclusion and Recommendations

Since EF Core 6 removed the direct implementation of IAsyncEnumerable<TEntity>, the compiler no longer faces ambiguity when selecting extension methods. Even if System.Linq.Async is still installed in the project, conflicts will no longer occur. Therefore, after upgrading to .NET 6, you can safely remove these redundant AsQueryable() calls to keep your code clean.

Change Log

  • 2024-07-16 Initial version created.