Skip to content

Instantly share code, notes, and snippets.

@raghuramn
Last active December 15, 2015 23:18
Show Gist options
  • Save raghuramn/5338853 to your computer and use it in GitHub Desktop.
Save raghuramn/5338853 to your computer and use it in GitHub Desktop.
public class AsyncQueryableAttribute : ActionFilterAttribute
{
private static readonly MethodInfo _bufferAsync = typeof(AsyncQueryableAttribute).GetMethod("BufferAsyncCore", BindingFlags.NonPublic | BindingFlags.Static);
private readonly QueryableAttribute _innerFilter = new QueryableAttribute();
// this method is copied from ActionFilterAttribute
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
cancellationToken.ThrowIfCancellationRequested();
HttpResponseMessage response = null;
Exception exception = null;
try
{
response = await continuation();
}
catch (Exception e)
{
exception = e;
}
try
{
HttpActionExecutedContext executedContext = new HttpActionExecutedContext(actionContext, exception) { Response = response };
_innerFilter.OnActionExecuted(executedContext);
if (executedContext.Response != null)
{
return await BufferAsyncQueryable(executedContext.Response);
}
if (executedContext.Exception != null)
{
throw executedContext.Exception;
}
}
catch
{
// Catch is running because OnActionExecuted threw an exception, so we just want to re-throw the exception.
// We also need to reset the response to forget about it since a filter threw an exception.
actionContext.Response = null;
throw;
}
throw new NotSupportedException("InnerFilter must set either Response or Exception. Both are null.");
}
// Executes the queryable eagerly and asynchronously if it implements IDbAsyncEnumerable.
// Note that this would call ToListAsync and would load the entire result set into memory.
private async Task<HttpResponseMessage> BufferAsyncQueryable(HttpResponseMessage response)
{
ObjectContent objectContent = response.Content as ObjectContent;
if (response.IsSuccessStatusCode && objectContent != null)
{
IQueryable queryable = objectContent.Value as IQueryable;
if (queryable != null && queryable is IDbAsyncEnumerable)
{
objectContent.Value = await BufferAsync(queryable);
}
}
return response;
}
private static Task<IQueryable> BufferAsync(IQueryable input)
{
MethodInfo bufferAsync = _bufferAsync.MakeGenericMethod(input.ElementType);
return bufferAsync.Invoke(null, new[] { input }) as Task<IQueryable>;
}
// called using reflection.
private static async Task<IQueryable> BufferAsyncCore<T>(IQueryable<T> input)
{
var list = await input.ToListAsync();
return list.AsQueryable();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment