According to the documentation for EF6, ObjectResult implements IDbAsyncEnumerable<T>
, IDbAsyncEnumerable - which should mean it implements asynchronous methods such as ObjectResult<T>
.ToListAsync(), right?
However, I'm not seeing that as a possible method in Visual Studio when calling a stored procedure like this:
public async Task<List<MyObject>> GetResultFromMyStoredProcedure(string foo, string bar)
{
return await context.My_Stored_Procedure(foo, bar).ToListAsync();
}
But calling the stored procedure as a query does seem to work:
public async Task<List<MyObject>> GetResultFromMyStoredProcedure(string foo, string bar)
{
var fooParam = new SqlParameter("@foo", foo);
var barParam = new SqlParameter("@bar", bar);
return await context.Database.SqlQuery<T>("My_Stored_Procedure @foo, @bar", fooParam, barParam).ToListAsync();
}
I've made sure my project is referencing the correct EF dll (6.1.3) - using NuGet. What am I missing?
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
You can use await Task. Yield(); in an asynchronous method to force the method to complete asynchronously. Insert it at beginning of your method and it will then return immediately to the caller and complete the rest of the method on another thread.
Since you can't just convert to Queryable, I've decompiled the internal methods that EF uses for IDbAsyncEnumerable
and made this extension method (out of the microsoft decompiled sources, so it should be as good as it gets):
public static Task<List<T>> ToListAsync<T>(this IDbAsyncEnumerable<T> source, CancellationToken cancellationToken)
{
TaskCompletionSource<List<T>> tcs = new TaskCompletionSource<List<T>>();
List<T> list = new List<T>();
ForEachAsync<T>(source.GetAsyncEnumerator(), new Action<T>(list.Add), cancellationToken).ContinueWith((Action<Task>)(t =>
{
if (t.IsFaulted)
tcs.TrySetException((IEnumerable<Exception>)t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(list);
}), TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
private static async Task ForEachAsync<T>(IDbAsyncEnumerator<T> enumerator, Action<T> action, CancellationToken cancellationToken)
{
using (enumerator)
{
cancellationToken.ThrowIfCancellationRequested();
if (await System.Data.Entity.Utilities.TaskExtensions.WithCurrentCulture<bool>(enumerator.MoveNextAsync(cancellationToken)))
{
Task<bool> moveNextTask;
do
{
cancellationToken.ThrowIfCancellationRequested();
T current = enumerator.Current;
moveNextTask = enumerator.MoveNextAsync(cancellationToken);
action(current);
}
while (await System.Data.Entity.Utilities.TaskExtensions.WithCurrentCulture<bool>(moveNextTask));
}
}
}
And you can have an overload without CancellationToken
like:
public static Task<List<T>> ToListAsync<T>(this IDbAsyncEnumerable<T> source)
{
return ToListAsync<T>(source, CancellationToken.None);
}
Not sure if I'm getting the right grip of the question, but can't you simply do this?
return await context.My_Stored_Procedure(foo, bar).AsQueryable().ToListAsync();
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