In EF Core, I'd like to intercept the expression right before it's being executed.
I thought of the interface IAsyncQueryProvider
and try to replace the default with my own implementation.
The IAsyncQueryProvider.CreateQuery
is invoked OK (right at the time creating the query) but the IAsyncQueryProvider.ExecuteAsync
(together with other overloads of Execute
) is not invoked at all.
I thought that this should be easily achieved with EF Core but looks like it is either still limited or requires some hidden knowledge.
Here's how I hook-up my own implementation of IAsyncQueryProvider
:
var efServices = new ServiceCollection();
efServices.AddEntityFrameworkSqlServer();
//find the default provider to remove it
var defaultProviderService = efServices.FirstOrDefault(e => e.ServiceType == typeof(IAsyncQueryProvider));
efServices.Remove(defaultProviderService);
efServices.AddScoped(defaultProviderService.ServiceType,
sp => {
var defaultProvider = ActivatorUtilities.CreateInstance(sp, defaultProviderService.ImplementationType) as IAsyncQueryProvider;
return new CustomQueryProvider(defaultProvider);
});
var efSp = efServices.BuildServiceProvider();
services.AddDbContext<MyDbContext>((sp,builder) => {
builder.UseInternalServiceProvider(efSp);
});
Here's the custom implemenation for IAsyncQueryProvider
:
public class CustomQueryProvider : IAsyncQueryProvider
{
private readonly IAsyncQueryProvider _proxy;
public CustomQueryProvider(IAsyncQueryProvider proxy)
{
_proxy = proxy;
}
public IQueryable CreateQuery(Expression expression)
{
return _proxy.CreateQuery(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
//code can be invoked here fine
return _proxy.CreateQuery<TElement>(expression);
}
public object Execute(Expression expression)
{
//never invoked here
return _proxy.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
//never invoked here
return _proxy.Execute<TResult>(expression);
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
{
//never invoked here
return _proxy.ExecuteAsync<TResult>(expression, cancellationToken);
}
}
It could be by design or requires some condition for those callbacks to be invoked.
So I don't hope that there is any fix here. But I do hope if there is some service type other than IAsyncQueryProvider
for me to implement (and replace the corresponding default) instead so that I can intercept the execution right at the time before calling the evaluating methods (ToList
, ToListAsync
, First
, FirstAsync
, ...)?