Getting started with the repository pattern in Silverlight

The repository pattern is quite a common design pattern when working with databases. And for a good reason as standardizing code on design patterns it makes it easer to read and understand.

The typical repository pattern in .NET

Most of the time the repository pattern looks something like this.

public interface IRepository<T>
{
    IEnumerable<T> All();
    IEnumerable<T> FindAll(Func<T, bool> exp);
}


Note that I am only implementing the loading of data. Normally there would also be methods for saving, deleting and crating new entities but I have left these out for now.



In regular .NET code that is just fine but in Silverlight not quite. The problem here is the fact that the functions return their result. Not uncommon but as most, if not all, IO actions in Silverlight are asynchronous returning something is not the best way. For Silverlight it is better to pass in a delegate as a callback when the action is complete.



So the logical implementation in Silverlight would be more like this where the action is whatever needs to be done when the request finishes.



public interface IRepository<T>
{
    void All(Action<IEnumerable<T>> action);
    void FindAll(Func<T, bool> filter, Action<IEnumerable<T>> action);
}


unfortunately that doesn’t quite work when we use an ADO.NET Data Service and the Entity Framework to get to our data. In that case the filter needs to be of type Expression<Func<T, bool>>. A slight complication but fortunately the calling code never needs to be aware of it. More on how to use an ADO.NET Data Service from Silverlight in this blog post.



 



The Silverlight implementation of the repository pattern



With that change the interface looks like this;



using System;
using System.Collections.Generic;
using System.Linq.Expressions;
 
namespace SilverlightRepository
{
    public interface IRepository<T>
    {
        /// <summary>
        /// Return all customers.
        /// </summary>
        /// <param name="action">What to do with the customers.</param>
        void All(Action<IEnumerable<T>> action);
 
        /// <summary>
        /// Return subset of customers.
        /// </summary>
        /// <param name="filter">The filter criteria.</param>
        /// <param name="action">What to do with the customers.</param>
        void FindAll(Expression<Func<T, bool>> filter, Action<IEnumerable<T>> action);
    }
}


 



Based upon this interface we can create an implementation to load customers. In this case I have already created an ADO.NET Data Service that serves customer data and set a service reference to it so the Customer class is generated.



My CustomerRepository class looks like this:



using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Browser;
using SilverlightRepository.NorthwindService;
 
namespace SilverlightRepository
{
    public class CustomerRepository: IRepository<Customer>
    {
        /// <summary>
        /// Return all customers.
        /// </summary>
        /// <param name="action">What to do with the customers.</param>
        public void All(Action<IEnumerable<Customer>> action)
        {
            var query = GetBaseQuery();
            DataServiceQuery<Customer> dsQuery = (DataServiceQuery<Customer>)query;
            dsQuery.BeginExecute(ar => action(dsQuery.EndExecute(ar)), null);
        }
 
        /// <summary>
        /// Return subset of customers.
        /// </summary>
        /// <param name="filter">The filter criteria.</param>
        /// <param name="action">What to do with the customers.</param>
        public void FindAll(Expression<Func<Customer, bool>> filter, Action<IEnumerable<Customer>> action)
        {
            var query = GetBaseQuery();
            query = query.Where(filter);
 
            DataServiceQuery<Customer> dsQuery = (DataServiceQuery<Customer>)query;
            dsQuery.BeginExecute(ar => action(dsQuery.EndExecute(ar)), null);
        }
 
        private static IQueryable<Customer> GetBaseQuery()
        {
            Uri uri = new Uri(HtmlPage.Document.DocumentUri, "NorthwindService.svc");
            NorthwindEntities context = new NorthwindEntities(uri);
 
            var query = from cust in context.CustomerSet
                        select cust;
 
            return query;
        }
    }
}


Note that the FindAll() function uses the same basic query as the All() function and just tags on the extra filter clause like this:



public void FindAll(
    Expression<Func<Customer, bool>> filter, 
    Action<IEnumerable<Customer>> action)
{
    var query = GetBaseQuery();
    query = query.Where(filter);
 
    DataServiceQuery<Customer> dsQuery = (DataServiceQuery<Customer>)query;
    dsQuery.BeginExecute(ar => action(dsQuery.EndExecute(ar)), null);
}


Nice and simple, just the way I like it [:)].



 



Using the CustomerRepository from a page













Loading all customers in the page loaded event is easy and done with these lines of code:



CustomerRepository repository = new CustomerRepository();
repository.All(customers => DataContext = customers);


Note that the ListBox is bound to the DataContext to load its items.



Now if we want to filter the code is almost as easy. Loading all customers from the UK is done with the following code:



CustomerRepository repository = new CustomerRepository();
    repository.FindAll(
        customer => customer.Country == "UK",
        customers => DataContext = customers);


The first lambda expression is the filter criteria and the second binds the DataContext for the ListBox. Using Fiddler we can see that when we ask for the UK customers only those customers are returned over the wire so we can be sure all filtering is done on the server.



image



 



Note that the client doesn’t need to know about the filter being of type Expression<Func<T, bool>>. All we need to do is just pass in simple lambda expression and the DataServiceQuery<T> knows how to handle it.



Enjoy!



6 thoughts on “Getting started with the repository pattern in Silverlight

  1. great work, spot on. I have a silly question: how do you deal with a query with join table in this repository pattern? For example: get customers, who has orders two days ago. Is that create another method to implement query based on the Data Services or any other suggestion. Another silly question: how can you put the cache manager in this pattern, for caching data purpose?

  2. If I have to massage that data, ie. let’s say I have a Silverlight object that I want to map the data to prior to binding – any suggestions?

  3. @Steve,

    I would suggest using the Model-View-ViewModel design pattern. The ViewModel is the place to massage the data.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>