Triggered by a blog post of Scott Hanselman I was wondering if I could create a generic extension method to asynchronously retrieve objects from a DataContext. And well I could. I ended up with two classes.
The ExecuteAsyncState:
1: using System;
2: using System.Collections.Generic;
3: using System.Data.Linq;
4: using System.Data.SqlClient;
5: using System.Threading;
6:
7: namespace AsyncDataAccess {
8: public class ExecuteAsyncState<T> {
9: public ExecuteAsyncState(DataContext dataContext, SqlCommand sqlCommand, Action<IEnumerable<T>> onReady) {
10: this.DataContext = dataContext;
11: this.SqlCommand = sqlCommand;
12: this.OnReady = onReady;
13: this.WaitHandle = new AutoResetEvent(false);
14: }
15:
16: public DataContext DataContext { get; private set; }
17: public SqlCommand SqlCommand { get; private set; }
18: public Action<IEnumerable<T>> OnReady { get; private set; }
19: public AutoResetEvent WaitHandle { get; private set; }
20: }
21: }
And the DataContextUtility:
1: using System;
2: using System.Collections.Generic;
3: using System.Data.Linq;
4: using System.Data.SqlClient;
5: using System.Linq;
6:
7: namespace AsyncDataAccess {
8: public static class DataContextUtility {
9: public static IAsyncResult ExecuteAsync<T>(this DataContext dataContext, IQueryable<T> query, Action<IEnumerable<T>> onReady) {
10: SqlCommand sqlCommand = dataContext.GetCommand(query) as SqlCommand;
11:
12: ExecuteAsyncState<T> asynchState = new ExecuteAsyncState<T>(dataContext, sqlCommand, onReady);
13:
14: AsyncCallback callback = new AsyncCallback(EndExecuteAsync<T>);
15: return sqlCommand.BeginExecuteReader(callback, asynchState);
16: }
17:
18: private static void EndExecuteAsync<T>(IAsyncResult result) {
19: ExecuteAsyncState<T> asynchState = result.AsyncState as ExecuteAsyncState<T>;
20:
21: DataContext dataContext = asynchState.DataContext;
22: SqlCommand sqlCommand = asynchState.SqlCommand;
23: Action<IEnumerable<T>> onReady = asynchState.OnReady;
24:
25: SqlDataReader reader = sqlCommand.EndExecuteReader(result);
26: var resultData = from item in dataContext.Translate<T>(reader)
27: select item;
28:
29: try {
30: onReady.Invoke(resultData);
31: }
32: finally {
33: reader.Close();
34: asynchState.WaitHandle.Set();
35: }
36: }
37: }
38: }
These two classes enable you to retrieve your object asynchronously. Like so:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Threading;
6:
7: namespace AsyncDataAccess {
8: class Program {
9: static void Main(string[] args) {
10: using (SampleDbContextDataContext context = new SampleDbContextDataContext()) {
11: context.Connection.Open();
12:
13: var customerQuery = from Customer c in context.Customers
14: select c;
15:
16: IAsyncResult customerResult =
17: context.ExecuteAsync<Customer>(customerQuery, (customers) => {
18: foreach (var c in customers) {
19: Console.WriteLine(c.ToString());
20: }
21: });
22:
23: var productQuery = from Product p in context.Products
24: select p;
25:
26: IAsyncResult productResult =
27: context.ExecuteAsync<Product>(productQuery, (products) => {
28: foreach (var p in products) {
29: Console.WriteLine(p.ToString());
30: }
31: });
32:
33: Console.WriteLine("Before the queries are returned:");
34:
35: ExecuteAsyncState<Customer> customerState = customerResult.AsyncState as ExecuteAsyncState<Customer>;
36: ExecuteAsyncState<Product> productState = productResult.AsyncState as ExecuteAsyncState<Product>;
37:
38: WaitHandle[] waitHandles = new[] { customerState.WaitHandle, productState.WaitHandle };
39: WaitHandle.WaitAll(waitHandles);
40:
41: Console.WriteLine("After the queries are returned:");
42:
43: Console.ReadLine();
44: }
45:
46: }
47: }
48: }
And that results in a screen like this:
Where you’ll see it prints my name first, then a product name, then the rest of the customer names to finish of with the rest of the product names.
It was very easy to get this result and you could of course add some more methods that simply return a single object instead of an IEnumerable<T>.
Cheers and have fun,
Wesley
Comments have been disabled for this content.