The only way that I know of to make an async call using Linq to SQL is to compose an IQueryable and then ask the data context for the equivalent SqlCommand using GetCommand, which provides the BeginExecuteReader method. This approach didn't work for stored procedures and required some hackery to essentially use reflection to generate the command based on the attribute-heavy method signature generated by the framework, which is how L2S does it internally anyway.
I'm now using the Entity Framework and found that there is no longer the GetCommand method and that it isn't using reflection to generate sproc calls anymore. Does anyone know how to retrieve the SQL or SqlCommand generated by EF in these cases, or alternatively a better way to make async database calls through EF?
Update
I still haven't found a way to achieve this for Sproc's but I have found an approach for IQueryable in EF. You can make an async call with the following (note these methods are in a class that extends the EF object context):
public IAsyncResult BeginSelectProcessedContent(
AsyncCallback callback, object asyncState, string foo) {
var processed = ProcessedContents
.Where(p => p.Foo == foo);
return DbUtil.BeginAsyncRequest(this, processed , callback, asyncState);
}
public ProcessedContent EndSelectProcessedContent(IAsyncResult asyncResult) {
return DbUtil.EndAsyncRequest(asyncResult, reader =>
this.Translate<ProcessedContent>(reader).SingleOrDefault());
}
The Utility code that enables this approach follows:
public static class DbUtil {
public static SqlCommand GetSqlCommand(this ObjectQuery q) {
SqlCommand command = new SqlCommand(q.ToTraceString());
foreach(ObjectParameter p in q.Parameters) {
command.Parameters.Add(new SqlParameter(p.Name, p.Value));
}
return command;
}
public static IAsyncResult BeginAsyncRequest(ObjectContext dataContext, IQueryable query, AsyncCallback callback, Object asyncState) {
SqlCommand command = ((ObjectQuery)query).GetSqlCommand();
return BeginAsyncRequest(dataContext, command, callback, asyncState);
}
public static IAsyncResult BeginAsyncRequest(ObjectContext dataContext, SqlCommand command, AsyncCallback callback, Object asyncState) {
AsyncSqlState sqlState = new AsyncSqlState(asyncState);
sqlState.BeginSqlCmd = command;
sqlState.BeginSqlCmd.Connection = (SqlConnection)((EntityConnection)dataContext.Connection).StoreConnection;
if(dataContext.Connection.State == System.Data.ConnectionState.Closed) {
dataContext.Connection.Open();
}
return sqlState.BeginSqlCmd.BeginExecuteReader(callback, sqlState, System.Data.CommandBehavior.CloseConnection);
}
public static T EndAsyncRequest<T>(IAsyncResult result, Func<SqlDataReader, T> returnConversion) {
SqlDataReader rdr = null;
try {
AsyncSqlState sqlState = (AsyncSqlState)result.AsyncState;
rdr = sqlState.BeginSqlCmd.EndExecuteReader(result);
return returnConversion(rdr);
} catch(Exception) {
throw;
} finally {
if(rdr != null) {
rdr.Close();
}
result.AsyncWaitHandle.Close();
}
}
}
public class AsyncSqlState {
public AsyncSqlState(Object asyncState) {
this.AsyncState = asyncState;
}
public SqlCommand BeginSqlCmd { get; set; }
public Object AsyncState { get; private set; }
}