I have a simple demo application that uses ADO.NET Data Services as a data service back end for a Silverlight application. My ADO.NET Data Service uses the Entity Framework to map the Northwind database tables of Customers, Orders, and Order Details. Once the Silverlight applications sets a service reference to the ADO.NET Data Service, you can use the client side LINQ libraries to build your application. My application looks like this, it has a drop down filled with customers, a grid with Order and a grid with Order Details. As you click on each one, it will filter the rest.
The LINQ statement for the drop down looks something like this:
1: //this uses the LINQ to REST proxy (servicereference1)
2: NorthwindEntities dat = new NorthwindEntities(
3: new Uri("Northwind.svc", UriKind.Relative));
4:
5: //linq query to get customers in ComboBox
6: var customers = from c in dat.Customers
7: orderby c.CustomerID
8: select c;
Pretty basic LINQ stuff. What I would like to do next is bind my drop down combobox to customers. There is one catch, since we are in Silverlight, this processing has to be done asynchronously, so that data binding code has to be done elseware.
There are a few ways to do this, the most straight forward it to set a delegate and catch an event, etc. Another is to use a code block and catch the event right in the same method.
While both of these solutions are fine, I don’t like them. I don’t like them because they look funny and pollute my data access code with tons of async communication stuff. Lastly, for each area where we have a LINQ statement, we have a lot of repetitive similar looking code. Every bone in my body wants to make that generic and only call it once.
Enter the AsyncLINQManager class I wrote. Forget about the details of this class for now, I will list it below in full. For now let’s show how to use the LINQ statement with the helper. First you have to create an instance of the AsyncLINQManager and then register an event. (No getting around the events!) You can do this in the page load handler:
1: //ref to the linq manager
2: alm = new AsyncLINQManager();
3: //register an event so we can do the databinding
4: alm.OnEntityFetched += Page_OnEntityFetched;
Now your LINQ statement needs one more line of code. Here is the same LINQ statement from above, passing customers to the AsyncLINQManager:
1: //this uses the LINQ to REST proxy (servicereference1)
2: NorthwindEntities dat = new NorthwindEntities(
3: new Uri("Northwind.svc", UriKind.Relative));
4:
5: //linq query to get customers in ComboBox
6: var customers = from c in dat.Customers
7: orderby c.CustomerID
8: select c;
9: //call async functions for the linq query
10: alm.LinqAsync(customers);
Line 10 is the only new line of code. Now the LINQ manager will take care of all of the async processing for us and we just have to put our data binding code in Page_OnEntityFetched() shown here:
1: //this event handler will do the actual databinding
2: void Page_OnEntityFetched(EntityEventArgument args)
3: {
4: switch (args.TypeName) //we get this info from the event
5: {
6: case "Customers":
7: CustomerCbo.ItemsSource = args.returnedList;
8: break;
9: case "Orders":
10: dg.ItemsSource=args.returnedList;
11: break;
12: case "Order_Details":
13: dg_Details.ItemsSource = args.returnedList;
14: break;
15:
16: }
17: }
You will notice that we do all of our data binding here, for all of our LINQ statements. This is the value of the AsyncLINQManager, now all of my binding code is in the same place. (I am sure that there will be some who disagree, but hey, build a better AsyncLINQManager and blog about it and I will link to it. :) )
So let’s take a look at the code to query the orders, you will notice that it will call the same LINQ manager and then have to come back to Page_OnEntityFetched() to do the binding:
1: //orders
2: private void AsyncBindOrdersCbo(string customerid)
3: {
4:
5: //this uses the LINQ to REST proxy (servicereference1)
6: NorthwindEntities dat = new NorthwindEntities(
7: new Uri("Northwind.svc", UriKind.Relative));
8:
9: //linq query to filter the Orders in the grid
10: var orders = from o in dat.Orders
11: where o.Customers.CustomerID == customerid
12: orderby o.OrderDate
13: select o;
14:
15: alm.LinqAsync(orders);
16:
17: }
What I really like is that you can go ahead and write a simple LINQ statement like you are use to, pass the result to the AsyncLINQManager for processing and then just have one event handler take care of all of your data binding. To me, your code is more clean and your developers can code the LINQ statements almost like normal (minus that one extra line of code) and forget about all of the async stuff.
The code for the AsyncLINQManager is here. All it is doing is sending out the async request, catching it, and then returning an IList and object name in the event args.
1: using System;
2: using System.Linq;//for IQueryable
3: using System.Data.Services.Client;//for DataServiceQuery
4: using System.Collections;//for ILIST
5:
6: namespace LinqUtilities
7: {
8: //ASYNC Linq stuff
9: public class AsyncLINQManager
10: {
11: //see the EntityEventArgument class below for the event args
12: public delegate void EntityFetchCompleted(EntityEventArgument args);
13: //developer must register this event in the UI code to catch the IList
14: public event EntityFetchCompleted OnEntityFetched;
15:
16: //pass in linq query object for execution
17: public void LinqAsync<T>(IQueryable<T> qry)
18: {
19: //generic async call to start the linq query
20: DataServiceQuery<T> dsq = (DataServiceQuery<T>)qry;
21: //Call the code async and assign OnFetchComplete to handle the result
22: dsq.BeginExecute(OnFetchComplete<T>, dsq);
23: }
24:
25: //method to handle the async result
26: void OnFetchComplete<T>(IAsyncResult result)
27: {
28: //catch the status of the async call
29: DataServiceQuery<T> dsq =(DataServiceQuery<T>)result.AsyncState;
30: //if we are done, then stuff the data into a untyped List
31: if (OnEntityFetched != null)
32: {
33: //delegate for event
34: OnEntityFetched(new EntityEventArgument
35: { returnedList = dsq.EndExecute(result).ToList() });
36: }
37: }
38:
39: }
40:
41:
42: //event args class for the event on the client to
43: //see what linq query they are handling
44: public class EntityEventArgument : EventArgs
45: {
46: public IList returnedList { get; set; }
47: public string TypeName
48: {
49: get { return returnedList.Count == 0 ? string.Empty : returnedList[0].GetType().Name; }
50: }
51:
52: }
53: }
You can download the sample code and the AsyncLINQManager code from my “DataAccess Hacks and Shortcuts” session demos here.
Enjoy!