i want to cast my IQueryable to an Interface like this:
public static IQueryable<T> _enableFilter<T>(this IQueryable<T> queryable) => queryable.Where(x => (x as IEnable).Enable);
_newsRepository.BaseQuery.EnableFilter().FirstOrDefaultAsync(x => x.Id == model.Id);
its work on EF core 2.2 but in 3 i give this error:
System.InvalidOperationException : The LINQ expression 'Where<News>(
source: DbSet<News>,
predicate: (n) => (n as IEnable).Enable)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
This query didn't work in EF Core 2.2 either. That cast has no meaning in SQL - there are no interfaces or inheritance in SQL. EF Core before 3 had an ... unfortunate feature, client side evaluation. If EF Core couldn't translate something to SQL, it would pull everything to the client and try to filter the data there. Needless to say, this is disastrous for performance. Worse, it did so without any warning. A warning or exception would allow you to recognize and fix the problem. At least, in EF Core 2.2 it was possible to disable client-side evaluation. In EF Core 3, this went away for good.
As for the method itself, you wouldn't need that cast at all if you used a type constraint, eg :
public static IQueryable<T> _enableFilter<T>(this IQueryable<T> queryable)
where T:IEnable
{
return queryable.Where(x => (x as IEnable).Enable);
}
I'm not sure EF Core will accept this - this is unusual syntax. It's a lot easier to just add that extra condition, eg :
_newsRepository.BaseQuery.EnableFilter().FirstOrDefaultAsync(x => x.Id == model.Id && x.Enabled);
Global Queries and Soft deletes
If you want to apply a filter condition to all queries using a specific entity, eg to implement soft-deletes, you can use global filters. In fact, soft-deletes is the first scenario mentioned in the docs.
In you context's OnModelCreating()
method you can add :
modelBuilder.Entity<SomeEntity>().HasQueryFilter(p => p.IsEnabled);
The reason behind this is a change in how EF Core 3 handles situations like this.
In EF core 2.x this was pulled into memory and the operation done there automatically, due to the potential performance issues of doing things like this the EF core team took the decision to move it from being the default behaviour to an error being the default behaviour.
Essentially, if you want the same behaviour as 2.x had, you now have to explicitly pull it into memory before doing it - however, this can often be a sign that its probably better to change how your query is done - unless you really do explicitly need this behaviour
For more specifics on the change see Here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With