# Tuesday, August 25, 2009

I have been a big fan of using the RESTful ADO.NET Data Services (aka Astoria). If you want to use Astoria out of the box without any modifications you really have to use the Entity Framework. But as long as you are using a data access library that supports IEnumerable and IQueryable (and IUpdateable to be useful for CRUD) you can use Astoria with just a little bit of extra work. 

Telerik OpenAccess supports LINQ and IUpdateable and can be used in Astoria as shown here. In a nutshell you have to first create your data access layer, then a web site to host your ADO .NET Data Service and then a client (in our case a Silverlight app.) You would have a solution that will look like this:

image

The problem is that if you use anything other than the Entity Framework, you need to do a little extra plumbing. In our case using OpenAccess, you have to create the IQueryable interfaces for all of your entities manually. You would need something like this:

   1:  public IQueryable<Customer> Customers
   2:  {
   3:      get
   4:      {
   5:          return this.scope.Extent<Customer>();
   6:      }
   7:  }

 

The scope variable you see on line # 5 is an instance of IObjectScope. IObjectScope is how OpenAcces implements the actual data context. According to the OpenAccess team, you have to add a few extra lines of code with these IQueryable interfaces into an “OADataContext” class. To be honest, I don’t want to do that, too much plumbing code.

Enter the OpenAccess WCF Wizard I wrote about before.

You can use the Wizard to generate the OADataContext file as well as the Astoria service file. Just point the wizard to the DAL layer file and select Astoria from the menu and generate the files and add them to the project (it is ok to overwrite the original CS file supporting the SVC file, just make sure you have the correct namespaces).

image

Now your Astoria service will look like this:

   1:      [System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
   2:      public class WebDataService : DataService<AstoriaDataContext>
   3:      {
   4:          protected override void HandleException(HandleExceptionArgs args)
   5:          {
   6:              base.HandleException(args);
   7:          }
   8:   
   9:          // This method is called only once to initialize service-wide policies.
  10:          public static void InitializeService(IDataServiceConfiguration config)
  11:          {
  12:              //let all the entities be full access (CRUD)
  13:              config.SetEntitySetAccessRule("*", EntitySetRights.All);
  14:          }
  15:      }
  16:  }

This is pretty standard Astoria stuff, line #13 does all of the Astoria magic.

Now let’s look at our Silverlight client.  First let’s create some XAML, I will just show you the DataGrid code here:

   1:  <data:DataGrid Grid.ColumnSpan="2" x:Name="dataGridCustomers" AutoGenerateColumns="False" ItemsSource="{Binding}">
   2:      <data:DataGrid.Columns>
   3:         <data:DataGridTextColumn Binding="{Binding Path=CompanyName}" Header="Company Name"></data:DataGridTextColumn>
   4:         <data:DataGridTextColumn Binding="{Binding Path=ContactName}" Header="Contact Name"></data:DataGridTextColumn>
   5:      </data:DataGrid.Columns>
   6:  </data:DataGrid>

 

As I wrote the other day, you still have to use a service and call the service asynchronously. In our Silverlight application we will then set a service reference to our Astoria Service, allowing us to write LINQ statements against the RESTful Astoria service.

image

Just like before we will have a LoadData() private method that our page load and “refresh” buttons will call. This will use LINQ to talk to the Astoria service and fetch all of the Customers. First we set up a LINQ data context (lines 4-5) and then a LINQ query (lines 8-9). Then we will use a code block (lines 13-14) to catch the async code (instead of a separate event handler) and perform the actual binding to the grid.

   1:  private void LoadData()
   2:  {
   3:      //this uses the LINQ to REST proxy (WebDataService)
   4:      AstoriaDataContext dat = new AstoriaDataContext(
   5:          new Uri("WebDataService.svc", UriKind.Relative));
   6:      
   7:      //link statement to get the data, can use WHERE, Orderby, etc
   8:      var customers = 
   9:          (from c in dat.Customers select c) as DataServiceQuery<WebDataService.Customer>;
  10:   
  11:      //use code block to perform the binding at the end of the async call
  12:      //casting the Customers to a LIST
  13:      customers.BeginExecute(
  14:          (ar) => dataGridCustomers.DataContext = customers.EndExecute(ar).ToList(), null);
  15:  }

When we run our code, we will get data right away:

image

We can also perform an update. We have a global collection called editedCustomers and we trap the BeginEdit method of the Silverlight grid and put an instance of each row (a Customer) that was edited into this collection. Then we provide an update button that will call UpdateData() shown below. UpdateData() will loop through each dirty customer in the editedCustomers collection (lines 8-11) and update them using the Astoria LINQ services (lines 10-11). Inside of our loop we are doing another code block (lines 12-25) to catch the async update. We use a counter to figure out when we are done (lines 16-22), since in an async model, your 3rd update out of 5 can be the last one to finish!

 

   1:  private void UpdateData()
   2:  {
   3:      //this uses the LINQ to REST proxy (WebDataService)
   4:      AstoriaDataContext dat = new AstoriaDataContext(
   5:          new Uri("WebDataService.svc", UriKind.Relative));
   6:   
   7:      //editedCustomers is a local collection containing only the dirty records
   8:      foreach (WebDataService.Customer customer in editedCustomers)
   9:      {
  10:          dat.AttachTo("Customers", customer);
  11:          dat.UpdateObject(customer);
  12:          //code block to handle the async call to updates
  13:          dat.BeginSaveChanges(
  14:              (ar) =>
  15:              {
  16:                  updatedCounter++;
  17:                  //once we are finished with all items in the collection, we are done
  18:                  if (updatedCounter == this.editedCustomers.Count)
  19:                  {
  20:                      MessageBox.Show("Customers have been changed successfull!", "Saving Changes", MessageBoxButton.OK);
  21:                      this.editedCustomers.Clear();
  22:                  }
  23:   
  24:                  dat.EndSaveChanges(ar);
  25:              }
  26:              , null);
  27:      }
  28:  }

 

That is it to build an Astoria based Silverlight application using OpenAccess. The OpenAccess WCF Wizard takes care of building the OpenAccess specific plumbing DataContext class for you as well as the Astoria service. Soon I will post another video by .NET Ninja in training Peter Bahaa on this demo as well as some more code examples using the WCF REST Toolkit and the OpenAccess WCF Wizard.

The code is available here. Enjoy!