Sunday, October 24, 2010

Developing Silverlight 4.0 App with MVVM and MEF

In the previous post we walked through the process of developing three tiers web app using Silverlight 4.0, MVVM and WCF Data services. In this post, we’ll see how we can take advantage of the new Managed Extensibility Framework (MEF) for Silverlight in order to loosen the coupling between the parts in the application and by that 1) simplify the development process, 2) make our application more extendable and 3) much more and testable.

MEF is specially important for Silverlight since it provides a way to reduce the app startup footprint and dramatically improve the user's startup experience by using the DeploymentCatalog (previously PackageCatalog) feature, which allows breaking the xap file into pieces, download the basic xap on initialization, and download the other xaps a-synchronously on demand or immediately after startup.

In the next post, we’ll develop a testing strategy for the demonstrated application, which includes component level and multi-component level tests.

MEF Overview

MEF introduces a great way to integrate the dependency injection pattern into an application. It provides a standard way for a component to export any of its parts (classes) to other components, and to import external parts from any component in the xap boundaries.

In a typical application, when the host component starts, it instruct MEF to satisfy the imports tree of its root object, as a result, MEF make use of automatic exports discovery protocol in order to locate and instantiate the required objects and to wire them together in the correct order.

In order to export a class, we simply decorate it with the Export attribute.

   1: [Export]
   2: public partial class MainPage : UserControl
   3: {
   4: }

We can also export a class through its interface.

   1: [Export(typeof(IBooksDataServiceProvider))]
   2: public class BooksDataServiceProvider: DataServiceProvider , IBooksDataServiceProvider
   3: {
   4: }

In order to import an object of a given type into a property/field, we use the import attribute.

   1: public partial class App : Application
   2: {
   3:     [Import]
   4:     public MainPage MainView
   5:     {
   6:         get { return RootVisual as MainPage; }
   7:         set { RootVisual = value; }
   8:     }
   9: }

We can also use the ImportingConstructor attribute to specify that all the parameters of a given constructor should be treated as imports.

   1: public class BooksViewModel : ViewModelBase
   2: {
   3:     [ImportingConstructor]
   4:     public BooksViewModel(IBooksDataServiceProvider booksProvider)
   5:     {
   6:     }

In order to instruct MEF container to initiate the process of locating required exports and populating imports, we call CompositionInitializer.SatisfyImports(..)

   1: public partial class App : Application
   2: {   
   3:     private void Application_Startup(object sender, StartupEventArgs e)
   4:     {
   5:         CompositionInitializer.SatisfyImports(this);
   6:     }
   7: }

In the sample above, we instructed MEF to satisfy the imports of the App object, as a result, MEF searches all the assemblies in the xap in order to locate the classes that need to be imported and there dependencies. Which practically means that MEF search for MainPage, and if MainPage imports other classes in its implementation, MEF will import them as well.

Dependency Injection     

The dependency injection (DI) design pattern is used to promote ‘Separation of concerns’, Modularity, Extensibility and Testability. It dictates that all the parts in the application will communicate with there dependencies though interfaces, and that every part will be injected with all of its dependencies, rejecting the use of static objects and Singletons in the application.

With DI, there’s always some kind of a ‘container’ that make sure that the parts are injected with there dependencies. When implementing the DI pattern without the use of a dedicated framework, the application instantiate the parts and wire them up together.

With MEF, we only have to declare that part A depends on part B (the dependency), and a framework ‘dependency injection container’ take care of instantiating Part B and injecting it to part A. This is particularly useful when part B is shared among multiple parts scattered around the application, in such case, we don’t have to keep track of part B instance and figure out how to distribute it to its consumers (for instance, by using the Singleton pattern), the framework take care of it for us.

The Application

The new MEF based application looks exactly like the application reviewed in the previous post, but with the addition of a new window that displays list of users (pulled out from the data storage using a dedicated data provider).

image

High Level Design

In the previous post we used the DalSevice singleton class as a Dependency Lookup in order to allow replacing the data access layer with a Test Double when required.

Since we’ve chosen to inject dependencies instead of hard wiring the parts and using Singletons - we no longer need a bridge between the presentation parts (ViewModels) and the data provides, so we can retire ourselves from the singleton DalService,

With MEF, every ViewModel simply declare the interface of the data provide/s that it depends on, and the ‘dependency injection container’ make sure that it’s injected with them.

Here’s the new design:          

image

Now lets see some code!

Since we already reviewed the main components of the application in the previous post, we’ll focus on the code that was added to integrate MEF into the application.

The View Code Behind
   1: [Export]
   2: public partial class BooksPage : Page
   3: {
   4:     [ImportingConstructor]
   5:     public BooksPage(BooksViewModel viewModel)
   6:     {
   7:         InitializeComponent();
   8:  
   9:         DataContext = viewModel;
  10:     }
  11: }

The BooksPage is exported through MEF to its host (the main page), and injected with BooksViewModel on construction (notice the ImportingConstructor attribute).

The ViewModel
   1: [Export]
   2:  public class BooksViewModel : ViewModelBase
   3:  {
   4:      [ImportingConstructor]
   5:      public BooksViewModel(IBooksDataServiceProvider booksProvider)
   6:      {
   7:          ... 
   8:  
   9:          UsersCommand = new DelegateCommand<object>(o1 =>
  10:          {
  11:              var window = UsersWindow.Value;
  12:              window.Show();
  13:          });
  14:      }
  15:  
  16:      [Import]
  17:      public Lazy<UsersWindow> UsersWindow { get; set; }
  18:     
  19:      public ICommand UsersCommand { get; private set; }
  20: }
  21:  

The ViewModel is exported (for the favor of BooksPage) through MEF. It’s injected with the BooksDataServiceProvider on construction and with UsersWinodws on demand (notice the Lazy<T> paradigm)

The BooksDataProvider
   1: [Export(typeof(IBooksDataServiceProvider))]
   2:  public class BooksDataServiceProvider: DataServiceProvider , IBooksDataServiceProvider
   3:  {
   4:      private readonly BookStore m_bookStore;
   5:  
   6:      [ImportingConstructor]
   7:      public BooksDataServiceProvider(IDalConfiguration configuration)
   8:      {
   9:          m_bookStore = new BookStore(configuration.BooksDataServiceRoot);
  10:      }
  11: }

Since the data provider need to be replaced with alternative implementations (for testing in our case), we export it through its interface. In addition, we need to be able to change its configuration so we added a new configuration class that is injected to the data provider on construction.

The DalConfiguration
   1: [Export(typeof(IDalConfiguration))]
   2: public class DalConfiguration : IDalConfiguration
   3: {
   4:     public DalConfiguration()
   5:     {
   6:         BooksDataServiceRoot = new Uri("http://localhost:16081/BookStoreDataService.svc");
   7:         UsersDataServiceRoot = new Uri("http://localhost:16081/UsersDataService.svc");
   8:  
   9:     }
  10:  
  11:     public Uri BooksDataServiceRoot { get; set; }
  12:     public Uri UsersDataServiceRoot { get; set; }
  13:  
  14: }

The DalConfiguration class defines the URIs of the available providers. It’s exported through interface to provide support for changing the data services URI.

Unit Tests

During unit tests, in order to test the presentation logic in isolation we simply inject the ViewModel with a test double (usually fake) instead of the real data provider.

   1: var providerFake = new BooksDataServiceProviderFake();
   2:  
   3: var booksViewModel = new BooksViewModel(providerFake);

In order to test presentation logic against real data access layer, we can inject the ViewModel with a real data provider, and configure the data provider to target a fake/mock data service

   1: var dataServiceMockUri = 
   2:     new Uri("http://localhost:21978/BookStoreDataServiceMock.svc");
   3:           
   4: var configuration =
   5:     new DalConfigurationFake { BooksDataServiceRoot = dataServiceMockUri };
   6:  
   7: var provider = new BooksDataServiceProvider(configuration);
   8:  
   9: BooksViewModel viewModel = new BooksViewModel(provider);

Unit tests should not make use of MEF container for building dependencies. As presented above, a typical test starts with instantiating a ViewModel, instantiating its dependencies (here we can replace the dependencies with test doubles) and injecting the ViewModel with its dependencies. In integration tests, we run the entire application and let MEF container do its job, naturally, we’ll prefer to include the real parts rather than using test doubles.

However, in some cases we might want to use test doubles in integration test, to accomplish that we need to replace the default CompositionContainer, and use the CatalogExportProvider to allow ‘overriding dependencies’ and to instruct MEF to use our test doubles instead of product implementations.

   1: var productCatalog = new AssemblyCatalog(...);
   2:  
   3: // Wrap the product catalog with CatalogExportProvider to enable overriding dependencies
   4: var exportProvider = new CatalogExportProvider(productCatalog);
   5:  
   6: var testCatalog = new TypeCatalog(typeof(BooksDataServiceProviderMock));
   7:  
   8: // Notice, the test catalog is placed before the product catalog, this instructs MEF container 
   9: // to choose test exports on top of product exports
  10: CompositionContainer container = new CompositionContainer(testCatalog, exportProvider);
  11:  
  12: exportProvider.SourceProvider = container;
  13: CompositionHost.Initialize(container);

The source code for the application and the unit tests can be downloaded from here

Wednesday, October 13, 2010

Developing Silverlight 4.0 Three Tiers App with MVVM and WCF Data Services (OData via ATOM)

In this post we’ll walk through the process of developing a three tiers, rich web application using Silverlight 4.0. As many applications built on top of WPF/Silverlight technologies, we’ll based the design on MVVM design pattern, and we’ll use WCF data services to query/update data exposed by a mid tier application.

In the next posts we’ll add the new Managed Extensibility Framework (MEF) to the application, and put together a testing strategy.

Silverlight 4.0

Silverlight introduces a quick and simple way to develop rich web applications. As oppose to traditional Web applications (developed with ASP.NET for instance), Silverlight is a client-side runtime environment, which means that the entire application is downloaded and run on the client machine, resulting a great user experience. With Silverlight, developers design xaml pages and target a managed API that is based on a subset of the .NET Framework (.NET Framework for Silverlight), traditionally using visual studio with C# and Visual Basic.

image

When the user connect to a Silverlight application, a .xap file that contains the application code and resources is being downloaded from the remote Silverlight host into the client machine. Since this can take quite a while, developers try to keep the xap file as small as possible by applying different patterns that aim to reduce the application code base.

When using MEF for Silverlight, developers can take advantage of the new DeploymentCatalog (previously PackageCatalog) that enables downloading only a portion of the code when the user connect, and adding code (downloading extra xap files) on demand as the application make progress.

MVVM

MVVM pattern, much like its equivalents MVC/P/X, separate the UI and the underlying presentation and business logic into three separate classes. The view, encapsulates the user interface and user interface logic. The view model encapsulates presentation logic and state, and the model encapsulates the application's business logic and data.

The view interacts with the view model, typically through data binding, commands, and change notification events. The view model, in turn, interacts with the model, acting as an intermediary between the view and the model and performing any necessary model-level data conversion and aggregation.

image

Silverlight/WPF provide power full binding infrastructure that makes MVVM a preferred choice for almost every application. In most cases, the ViewModel is assigned as the DataContext of its pair View, allowing every control on the View to easily bind any of its properties to a matching property on the ViewModel. In addition,Instead of implementing event handlers (such as button click) in the VIew code behind, the View can bind to Command objects exposed by its ViewModel.

In the other direction, the ViewModel notifies the View of any state changes via change notification events through INotifyPropertyChanged and INotifyCollectionChanged interfaces, and about data validation via the IDataErrorInfo or INotifyDataErrorInfo interfaces.

Often enough, Model objects implement the INotifyPropertyChanged, INotifyCollectionChanged and IDataErrorInfo interfaces in order to notify the View on state change on their side, since the Views have no direct link the the Models, the notifications are being propagated from the Model to the View through the ViewModel .

While implementing the MVVM pattern, we need to figure out whether the View instantiate the VIewModel on the code behind, or whether a third-party component hook the ViewModel to the View. If you’re looking for an answer in MVVM spec, you will not find it there! that’s for you to decide after looking at the overall application architecture. The natural way is to let the View create its ViewModel, however, applications that use Dependency injection technique (to loosen the coupling between the components ) will prefer to inject the ViewModel (perhaps by using dependency injection container) to the View.

WCF RIA Services

Typical Silverlight application runs solely on the client machine, unlike traditional Web applications, there’re no automatic round trips to the server for fetching/submitting data and for updating the display. However, many Silverlight applications need to establish communication with back end server/s, for instance, to initiate remote action/calculation or to query/manipulate some distanced data. To accomplish this, one can make use of the WCF RIA Services technology that provides framework components, tools, and services that make the application logic on the server available to the Silverlight client without having to manually duplicate that programming logic.

image

WCF Data Services

While the WCF RIA Services was designed specifically for end-to-end Silverlight solutions (where client & server are designed and deployed together), many Silverlight applications require loosely coupled services that can be targeted/reused by other applications. 

With WCF Data Services, developers design services that are accessible through a REST-based (HTTP) protocol called OData (www.odata.org) , thus can be consumed by different applications of different platform (Java, PHP, .NEY, etc.).

The WCF Data Services environment provides .NET developers with a server framework for creating REST-based data-centric web services on top of data model with appropriate security, and client libraries for accessing those services.

WCF Data Services can expose data (as OData feeds) that originates from various sources. One can create an OData based service by using an ADO.NET Entity Framework data model, by using the LinqToSql technology, or by using custom provider that uses standard CLR classes. In this walkthrough, we’ll implement a custom provider that exposes a data model that describes a book store, and support update operations. 

image

WCF Data Services v.s. WCF RIA Services

Obviously, there’s an overlap between RIA Services for Silverlight and WCF Data Services support for Silverlight. So basic guideline is to use WCF RIA Services for traditional operation-based services and use WCF Data Services for a more REST-based approach. WCF RIA Services is supposed to eventually also support OData.

WCF Data Services v.s. WCF Services

While WCF Data Services/OData is intended for services that primarily expose data with few (if any) service operations, straight WCF is more applicable for services that primarily provide service operations with data being only a small consideration.

The Application - Client Side

In this walkthrough, we’ll dive through the implementation of the following 3-tiers application:

image

The application includes a simple View that presents the books that are available in the book store. A mid tier based on WCF data services that allows querying/updating books, and make use of a repository that pull out the books from the data storage and allows updating the metadata associated with the books.

Lets see how the main parts in the application collaborate.

image

The workflow starts from the ViewModel, which makes queries/performs updates on the DalService, which propagate the call to the DataServiceProvider, which send http GET/UPDATE to the data service, that propagate the request/command to the repository.

Let see how the application main end2end sequence (getting books from the data storage) looks like:

image

As the sequence describes, the View (BooksPage) prompt the ViewModel to refresh its state, in turn, the ViewModel calls GetBook on the DelService, passing it with  a delegate to be invoked when the books arrive. The DalService propagate the request to the DataServiceProvider (through its abstraction), which construct a query and pass it to the web service over HTTP. The web service turn to the repository (through its abstraction) that fetch the data from the storage and return query able collection of books. The result is being transferred back to the client application, and travels all the way back to the ViewModel, which in turn update its internal books collection and notify the BooksPage that its state has changed through the INotifyPropertyChanged interface.

Since the data service is REST based, it is being queried using standard http GET request. In response, it returns the data as an Atom-Pub feed. Lets look at the generated http traffic using our good friend Fiddler.

The Request Header:

   1: GET /BookStoreDataService.svc/BooksGET /BookStoreDataService.svc/Books HTTP/1.1 HTTP/1.1

The Response Body:

   1: HTTP/1.1 200 OK
   2: Server: ASP.NET Development Server/10.0.0.0
   3: Date: Wed, 13 Oct 2010 11:03:36 GMT
   4: X-AspNet-Version: 4.0.30319
   5: DataServiceVersion: 1.0;
   6: Content-Length: 2023
   7: Cache-Control: no-cache
   8: Content-Type: application/atom+xml;charset=utf-8
   9: Connection: Close
  10:  
  11: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
  12: <feed xml:base="http://127.0.0.1:16081/BookStoreDataService.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  13:   <title type="text">Books</title>
  14:   <id>http://127.0.0.1:16081/BookStoreDataService.svc/Books</id>
  15:   <updated>2010-10-13T11:03:36Z</updated>
  16:   <link rel="self" title="Books" href="Books" />
  17:   <entry>
  18:     <id>http://127.0.0.1:16081/BookStoreDataService.svc/Books('Book1')</id>
  19:     <title type="text"></title>
  20:     <updated>2010-10-13T11:03:36Z</updated>
  21:     <author>
  22:       <name />
  23:     </author>
  24:     <link rel="edit" title="Book" href="Books('Book1')" />
  25:     <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Authors" type="application/atom+xml;type=feed" title="Authors" href="Books('Book1')/Authors" />
  26:     <category term="PrototypeMVVM.Web.Core.Models.Book" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
  27:     <content type="application/xml">
  28:       <m:properties>
  29:         <d:Name>Book1</d:Name>
  30:         <d:Description>Aviad</d:Description>
  31:       </m:properties>
  32:     </content>
  33:   </entry>
  34:   <entry>
  35:     <id>http://127.0.0.1:16081/BookStoreDataService.svc/Books('Book2')</id>
  36:     <title type="text"></title>
  37:     <updated>2010-10-13T11:03:36Z</updated>
  38:     <author>
  39:       <name />
  40:     </author>
  41:     <link rel="edit" title="Book" href="Books('Book2')" />
  42:     <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Authors" type="application/atom+xml;type=feed" title="Authors" href="Books('Book2')/Authors" />
  43:     <category term="PrototypeMVVM.Web.Core.Models.Book" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
  44:     <content type="application/xml">
  45:       <m:properties>
  46:         <d:Name>Book2</d:Name>
  47:         <d:Description></d:Description>
  48:       </m:properties>
  49:     </content>
  50:   </entry>
  51: </feed>

Note:  you can’t use ‘localhost’ in the URI if you want to see the http traffic on fiddler (.NET bypass the fiddler proxy for "localhost" addresses). To workaround this, replace the ‘localhost’ with ‘ipv4.fiddler’ (when fiddler sees a request bound for "ipv4.fiddler", it simply changes it to 127.0.0.1).

Now lets see some code!

The View Code Behind
   1: public partial class BooksPage : Page
   2: {
   3:     public BooksPage()
   4:     {
   5:         InitializeComponent();
   6:  
   7:         DataContext = new BooksViewModel();
   8:     }
   9: }
Since all the presentation logic is placed in the VIewModel, the only thing left to do is to instantiate the ViewModel and to assign it as the DataContext of the View. Notice that the View includes no event handler implementations (the Commands in the ViewModel handle those), no link to the data layer and no assignments of values to named controls.
This greatly promotes testability since it allows mocking the View in most of the tests, which will naturally surround the VIewModel. And yet still, in order to achieve full test coverage, we’ll need to ensure that the View xaml is right, meaning that all the appropriate properties  of its controls are bound to the right properties of the ViewModel. To achieve this, we’ll have to write extra tests that include a real View, real ViewModel and fake everything else, only to verify that the link between the View and the ViewModel is correct.
The View axml
   1: <navigation:Page x:Class="PrototypeMVVM.Core.Views.BooksPage" 
   2:            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:            mc:Ignorable="d"
   7:            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
   8:            d:DesignWidth="248" d:DesignHeight="298"
   9:            Title="Page1 Page" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" >
  10:     <Grid x:Name="LayoutRoot" Height="490" Width="776" >
  11:         <sdk:Label Content="Books" Height="28" HorizontalAlignment="Left" Margin="16,9,0,0" 
  12:                    VerticalAlignment="Top" Width="75" />
  13:         <sdk:DataGrid ItemsSource="{Binding Path=Books}" 
  14:                       AutoGenerateColumns="False" Height="216" HorizontalAlignment="Left" 
  15:                       Margin="16,32,0,0" VerticalAlignment="Top" Width="216">
  16:             <sdk:DataGrid.Columns>
  17:                 <sdk:DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" Width="SizeToHeader" />
  18:                 <sdk:DataGridTextColumn Binding="{Binding Path=Description}" Header="Description" Width="SizeToHeader" />
  19:             </sdk:DataGrid.Columns>
  20:         </sdk:DataGrid>
  21:         <Button ToolTipService.ToolTip="{Binding Path=SaveButtonToolTip}"
  22:                 Height="23" HorizontalAlignment="Right" Margin="0,254,699,0" 
  23:                 VerticalAlignment="Top" Width="61" Command="{Binding Path=SaveCommand}" Content="Save" />
  24:     </Grid>
  25: </navigation:Page>

Since the ViewModel is assigned as the DataContext of the View, in order to apply binding, we only need to specify the name of the ViewModel property. For instance, in order to bind the ‘Save’ button tool tip to the ‘SaveButtonToolTip’ property of the VIewModel - we use the following syntax:  {Binding Path=SaveButtonToolTip}.

The VIewModel class (where the bulk of the code reside)
   1: public class BooksViewModel : ViewModelBase
   2: {
   3:     private IList<BookViewRow> m_books;
   4:  
   5:     public BooksViewModel()
   6:     {
   7:         SaveButtonToolTip = "Save Changes";
   8:         Refresh();
   9:  
  10:         SaveCommand = new DelegateCommand<object>(o => DalService.Instance.Save());
  11:     }
  12:  
  13:     public ICommand SaveCommand { get; private set; }
  14:  
  15:     public string SaveButtonToolTip { get; private set; }
  16:     
  17:     public IList<BookViewRow> Books
  18:     {
  19:         get
  20:         {
  21:             return m_books;
  22:         }
  23:         private set 
  24:         {
  25:             m_books = value;
  26:             RaisePropertyChanged(() => Books);
  27:         }
  28:     }
  29:  
  30:     public void Refresh()
  31:     {
  32:         DalService.Instance.GetBooks(
  33:             (sender, args) => LoadBookse(args.Result));
  34:     }
  35:  
  36:     private void LoadBookse(IEnumerable<Book> books)
  37:     {
  38:         List<BookViewRow> bookViewRows = books.Select(book => new BookViewRow(book)).ToList();
  39:  
  40:         Books = bookViewRows;
  41:     }
  42: }

The ViewModel exposes ‘SaveButtonToolTip’ and ‘Books’ properties that are bound (in the xaml) to the button tooltip provider and to the data grid respectively. The SaveCommand property is bound to the Command property of the ‘Save’ button, such that when the user click on the button, ‘Execute’ is being called on the ICommand object exposed by SaveCommand property.

Additionally, the ViewModel is in charge of getting books from the DalService. As you can see, it initiate an a-sync call to the DalService, and when the response arrive, it updates the Books property which update a private m_books field and notify the View about the change through the INotifyPropertyChanged interface.

The Books property is a List of objects of the type BookViewRow, which defines the visual representation of the data grid by exposing a property for every column on the data grid surface. As you can see in the xaml, every grid column is bound to a different property of the BookViewRow class.

In addition, you may also notice that the setters of each of the properties includes call to  DalService.Instance.UpdateObject(m_Book). As a result, whenever the user changes value of a cell on the grid surface, the DalService is being updated. We’ll revisit this little piece of code latter on.

   1: public class BookViewRow : INotifyPropertyChanged
   2: {
   3:     private readonly Book m_Book;
   4:  
   5:     public BookViewRow(Book Book)
   6:     {
   7:         m_Book = Book;
   8:     }
   9:  
  10:     public string Name
  11:     {
  12:         get { return m_Book.Name; }
  13:         set
  14:         {
  15:             m_Book.Name = value;
  16:             DalService.Instance.UpdateObject(m_Book);
  17:         }
  18:     }
  19:  
  20:     public string Description
  21:     {
  22:         get
  23:         {
  24:             return m_Book.Description;
  25:         }
  26:         set
  27:         {
  28:             m_Book.Description = value;
  29:             DalService.Instance.UpdateObject(m_Book);
  30:         }
  31:     }
  32:  
  33:     public event PropertyChangedEventHandler PropertyChanged
  34:     {
  35:         add
  36:         {
  37:             m_Book.PropertyChanged += value;
  38:         }
  39:         remove
  40:         {
  41:  
  42:             m_Book.PropertyChanged -= value;
  43:         }
  44:     }
  45: }

As you can see, the VIewModel derive from ViewModelBase class which encapsulate a mechanism for updating the View when ever a property on the ViewModel change.

   1: public class ViewModelBase : INotifyPropertyChanged
   2: {
   3:     protected void RaisePropertyChanged<TViewModel>(Expression<Func<TViewModel>> property)
   4:     {
   5:         var expression = property.Body as MemberExpression;
   6:         var member = expression.Member;
   7:  
   8:         if (PropertyChanged != null)
   9:         {
  10:             PropertyChanged(this, new PropertyChangedEventArgs(member.Name));
  11:         }
  12:     }
  13:  
  14:     public event PropertyChangedEventHandler PropertyChanged;
  15: }

In addition, instead of writing a new SaveCommand class and assign it to the SaveCommand property, the latter property in assigned to an instance of DelegateCommand, which is constructed with a delegate to a method in the ViewModel that implement the save operation. This allows us to implement the command logic in the ViewModel without having to create a concrete command class that calls the appropriate method on the ViewModel.

   1: public class DelegateCommand<T> : ICommand
   2: {
   3:     private readonly Action<T> m_executeAction;
   4:  
   5:     private readonly Func<T, bool> m_canExecuteAction;
   6:  
   7:     public DelegateCommand(Action<T> executeAction)
   8:         : this(executeAction, null)
   9:     {
  10:     }
  11:  
  12:     public DelegateCommand(Action<T> executeAction, Func<T, bool> canExecuteAction)
  13:     {
  14:         m_executeAction = executeAction;
  15:         m_canExecuteAction = canExecuteAction;
  16:     }
  17:  
  18:     public event EventHandler CanExecuteChanged;
  19:  
  20:     public bool CanExecute(object parameter)
  21:     {
  22:         if (m_canExecuteAction != null)
  23:         {
  24:             return (m_canExecuteAction((T)parameter));
  25:         }
  26:  
  27:         return m_executeAction != null;
  28:     }
  29:  
  30:     public void Execute(object parameter)
  31:     {
  32:         if (m_executeAction != null)
  33:         {
  34:             m_executeAction((T)parameter);
  35:         }
  36:     }
  37: }
The DalService

The DalService is a Singleton that’s accessible to all the parts in the application, its job is to abstract away the communication with the data services. It defines the URI of the data service and encapsulates a replaceable DataServiceProvider that communicate directly to the data service.

   1: public class DalService : IIDalServiceTesting
   2: {
   3:     IDataServiceProvider m_dataServiceProvider;
   4:     private Uri m_datServiceRoot = new Uri("http://localhost:16081/BookStoreDataService.svc");
   5:  
   6:     static readonly DalService s_instance = new DalService();
   7:     public static DalService Instance
   8:     {
   9:         get { return s_instance; }
  10:     }
  11:  
  12:     private IDataServiceProvider DataServiceProvider
  13:     {
  14:         get
  15:         {
  16:             return m_dataServiceProvider ?? (m_dataServiceProvider = new DataServiceProvider(m_datServiceRoot));
  17:         }
  18:     }
  19:  
  20:  
  21:     public void GetBooks(EventHandler<ResponseEventArgs<IEnumerable<Book>>> handler)
  22:     {
  23:         DataServiceProvider.GetBooks(handler);  
  24:     }
  25:  
  26:     public void Save()
  27:     {
  28:         DataServiceProvider.Save();
  29:     }
  30:  
  31:     public void UpdateObject(Book book)
  32:     {
  33:         DataServiceProvider.UpdateObject(book);
  34:     }
  35:  
  36:     void IIDalServiceTesting.SetDataServiceRoot(Uri uri)
  37:     {
  38:         m_datServiceRoot = uri;
  39:     }
  40:  
  41:     void IIDalServiceTesting.SetDataServiceProvider(IDataServiceProvider provider)
  42:     {
  43:         m_dataServiceProvider = provider;
  44:     }
  45: }

The DalSevice allows 1) fetching books asynchronously through ‘GetBooks’, 2) notifying the ‘WCF data services client runtime’ that a book has changed though ‘UpdateObject’, and 3) saving the changes made on the books through ‘Save’. All the operations that were mentioned are implemented in the underlying IDataServiceProvider object.

The DalService allows test units to replace its underlying DataServiceProvider with an alternative implementation or an empty one through SetDataServiceProvider, which belongs to the IDalServiceTesting interface and implemented explicitly.

Since IDalServiceTesting is implemented explicitly, callers must cast the DalService to IDalServiceTesting  in order to call SetDataServiceProvider.

   1: IIDalServiceTesting dalServiceTesting = DalService.Instance;
   2: dalServiceTesting.SetDataServiceProvider(new DataServiceProviderNull());

This a good practice since it hides away the DalService test related functionality, and it discourage developers  from calling this code from non-test code.

Another thing to notice is that we can change the URI of the data service, also through the IDalServiceTesting interface. This can be used in order to replace the data service host with a test host, which will allow us to plug in alternative implementation and to test the DalService in isolation.

The DataServiceProvider
   1: public class DataServiceProvider : IDataServiceProvider
   2: {
   3:     class RequestInfo<T>
   4:     {
   5:         public IQueryable<T> Query { get; private set; }
   6:         public EventHandler<ResponseEventArgs<IEnumerable<T>>> Handler { get; private set; }
   7:  
   8:         public RequestInfo(IQueryable<T> query, EventHandler<ResponseEventArgs<IEnumerable<T>>> handler)
   9:         {
  10:             Query = query;
  11:             Handler = handler;
  12:         }
  13:     }
  14:  
  15:     private readonly BookStore m_bookStore;
  16:  
  17:     public DataServiceProvider(Uri serviceRoot)
  18:     {
  19:         m_bookStore = new BookStore(serviceRoot);
  20:     }
  21:  
  22:  
  23:     public void GetBooks(EventHandler<ResponseEventArgs<IEnumerable<Book>>> handler)
  24:     {
  25:         var query = from g in m_bookStore.Books
  26:                     select g;
  27:  
  28:         var dataServiceQuery = (DataServiceQuery<Book>)query;
  29:         dataServiceQuery.BeginExecute(OnBooksArrived, new RequestInfo<Book>(query, handler));
  30:  
  31:     }
  32:  
  33:     private void OnBooksArrived(IAsyncResult ar)
  34:     {
  35:         var requestInfo = (RequestInfo<Book>)ar.AsyncState;
  36:         EventHandler<ResponseEventArgs<IEnumerable<Book>>> handler = requestInfo.Handler;
  37:         var query = (DataServiceQuery<Book>)requestInfo.Query;
  38:         IEnumerable<Book> books = query.EndExecute(ar);
  39:         if (books == null)
  40:         {
  41:             handler(this, new ResponseEventArgs<IEnumerable<Book>> ("Response returned blanck"));
  42:             return;
  43:         }
  44:  
  45:         handler(this, new ResponseEventArgs<IEnumerable<Book>> (books));
  46:     }
  47:  
  48:     public void Save()
  49:     {
  50:         //m_BookStore.AddToBooks(new Book() { DbName = "ssss" + s_counter++});
  51:         m_bookStore.BeginSaveChanges(SaveChangesOptions.ReplaceOnUpdate, SaveCallback, null);
  52:     }
  53:  
  54:     public void UpdateObject(Book book)
  55:     {
  56:         m_bookStore.UpdateObject(book);
  57:     }
  58:  
  59:     private void SaveCallback(IAsyncResult ar)
  60:     {
  61:         m_bookStore.EndSaveChanges(ar);
  62:     }
  63: }

The DataSevicePorvider instantiate a BookStore class injecting it with the URI of the remote service. The BookStore is an auto generated proxy that was created by ‘WCF data services client runtime’ based on the matching server object exposed by the data service (we’ll get into it shortly). The BookStore proxy is the root proxy, and as such it is used to query for books and to save the changes made to the in-memory books collection.

Notice that unlike the full version of .NET Framework, .NET for Silverlight only support asynchronous  queries and asynchronous saving.

The Application - Server Side

The server side application is all about providing REST-full data services to the clients.

As mentioned above, we’ll not make use of the entity framework, we’ll create our very own data model and implement a custom update service provider that will allow the clients to update the data, which in turn will be stored in an xml based data storage.  

So, lets start. A typical WCF data service provider is hosted by a web project that contains special class that look like this:

   1: public class BookStoreDataService :  DataService<BookStore>, IServiceProvider
   2: {
   3:     public static void InitializeService(DataServiceConfiguration config)
   4:     {
   5:         config.SetEntitySetAccessRule("*", EntitySetRights.All);
   6:         config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
   7:         config.DataServiceBehavior.AcceptProjectionRequests = true;
   8:     }
   9:  
  10:     public object GetService(Type serviceType)
  11:     {
  12:         if (serviceType == typeof(IDataServiceUpdateProvider))
  13:         {
  14:             return new BookStoreUpdateProvider(CurrentDataSource);
  15:         }
  16:         return null;
  17:     }
  18: }

The service derive form DataSevice<T>, where T defines the root class of the object model that is being exposed. In our case, it will expose the BookStore class.

In the above implementation, the  data service is implemented in the web project. In order to enable better testing experience, we’ll do things differently. We’ll keep the web project as thin as possible, and move all the juicy implementation to a separate assembly. This way we can replace the web project with a test web project, which will allow us to host the test framework and the WCF data service from the same web project, and to switch from real data service to mock data service without having to load different projects.

The BookStoreDataService
   1: // BookStoreDataServiceCore resides in a separate assembly
   2:  public class BookStoreDataService : BookStoreDataServiceCore    
   3:  {
   4:      public static void InitializeService(DataServiceConfiguration config)
   5:      {
   6:          config.SetEntitySetAccessRule("*", EntitySetRights.All);
   7:          config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
   8:          config.DataServiceBehavior.AcceptProjectionRequests = true;
   9:      }
  10:  }
The BookStoreDataServiceCore
   1: // We use custom data provider
   2:    public class BookStoreDataServiceCore : DataService<BookStore>, IServiceProvider
   3:    {
   4:        protected override BookStore CreateDataSource()
   5:        {
   6:            var authors = new List<Author> 
   7:                {new Author("Mark"), new Author("Sarit"), new Author("John")};
   8:  
   9:            var repository = RepositoryBuilder.Create<Book>("Books.xml");
  10:  
  11:            return new BookStore(repository, authors);
  12:        }
  13:  
  14:        public object GetService(Type serviceType)
  15:        {
  16:            // We provide only update services..
  17:            if (serviceType == typeof(IDataServiceUpdateProvider))
  18:                return new BookStoreUpdateProvider(CurrentDataSource);
  19:  
  20:            return null;
  21:        }
  22:    }

Since the BookStoreDataServiceCore derive from DataService<BookStore> – we need to implement the CreateDataSource method in order to instantiate the BookStore object, which is the root of our data model (if will not implement it – the runtime will create a default instance of the BookStore for us). In order to enable testing the data model in isolation, we inject the BookStore with a replaceable repository object that encapsulates the CRUD logic.

In addition, we support updates on the data model through BookStoreUpdateProvider that is retuned by GetService.

The  BookStoreUpdateProvider
   1: public class BookStoreUpdateProvider : IDataServiceUpdateProvider
   2: {
   3:     private readonly BookStore m_bookStore;
   4:     private readonly List<Action> m_pendingChanges = new List<Action>();
   5:     public BookStoreUpdateProvider(BookStore bookStore)
   6:     {
   7:         m_bookStore = bookStore;
   8:     }
   9:  
  10:     public object CreateResource(string containerName, string fullTypeName)
  11:     {
  12:         var book = new Book();
  13:         // And register pending change to add the resource to the resource set list
  14:         m_pendingChanges.Add(() => m_bookStore.Add(book));
  15:  
  16:         return book;
  17:     }
  18:  
  19:     public object GetResource(IQueryable query, string fullTypeName)
  20:     {
  21:         object resource = Enumerable.Cast<object>(query).FirstOrDefault();
  22:  
  23:         return resource;
  24:     }
  25:  
  26:     public object ResetResource(object resource)
  27:     {
  28:         return resource;
  29:     }
  30:  
  31:     public void SetValue(object targetResource, string propertyName, object propertyValue)
  32:     {
  33:         PropertyInfo propertyInfo = targetResource.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
  34:         m_pendingChanges.Add(() => propertyInfo.SetValue(targetResource, propertyValue, null));
  35:  
  36:     }
  37:  
  38:     public object GetValue(object targetResource, string propertyName)
  39:     {
  40:         return null;
  41:     }
  42:  
  43:     public void SetReference(object targetResource, string propertyName, object propertyValue)
  44:     {
  45:     }
  46:  
  47:     public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
  48:     {
  49:     }
  50:  
  51:     public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
  52:     {
  53:     }
  54:  
  55:     public void DeleteResource(object targetResource)
  56:     {
  57:     }
  58:  
  59:     public void SaveChanges()
  60:     {
  61:         if (m_pendingChanges.Count == 0) return;
  62:  
  63:         foreach (var pendingChange in m_pendingChanges)
  64:         {
  65:             pendingChange();
  66:         }
  67:  
  68:         m_pendingChanges.Clear();
  69:         m_bookStore.SubmitChanges();
  70:     }
  71:  
  72:     public object ResolveResource(object resource)
  73:     {
  74:         return resource;
  75:     }
  76:  
  77:  
  78:     public void ClearChanges()
  79:     {
  80:     }
  81:  
  82:     public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
  83:     {
  84:  
  85:     }
  86: }

Every time that the client make changes to a proxy object and call UpdateObject on the root proxy (using  DalService.Instance.UpdateObject, see BookViewRow implementation above) –  the SetValue method is called on the BookStoreUpdateProvider, which in turn add invocation delegate to the pending changes list. Once BeginSaveChanges is called on the root proxy (using  DalService.Instance.Save) – the SaveChanges method is called , which in turn execute all the pending invocations and calls SubmitChanges on the server side BookStore object, which in turn uses the repository object to persist the changes.

When the SaveChanges method is called, the client sends changes back to the data service. SaveChanges can fail when data changes in the client conflict with changes (made by other clients) in the data service. OData provides some support for optimistic concurrency that enables the data service to detect update conflicts (by using Concurrency tokens, which are included in the eTag header of requests to and responses from the data service, and managed by the WCF Data Services  client).

The BookStore
   1: public class BookStore 
   2:   {
   3:       private readonly IRepository<Book> m_repository;
   4:       private readonly List<Author> m_authors;
   5:  
   6:       public BookStore(IRepository<Book> repository, List<Author> authors)
   7:       {
   8:           m_repository = repository;
   9:           m_authors = authors;
  10:       }
  11:  
  12:       public IQueryable<Book> Books
  13:       {
  14:           get { return m_repository.Query(); }
  15:       }
  16:  
  17:       public IQueryable<Author> Tables
  18:       {
  19:           get
  20:           {
  21:               return m_authors.AsQueryable();
  22:           }
  23:       }
  24:  
  25:  
  26:       public void Add(Book book)
  27:       {
  28:           m_repository.Add(book);
  29:       }
  30:  
  31:       public void SubmitChanges()
  32:       {
  33:           m_repository.SubmitChanges();
  34:       }
  35:   }

Notice that the BookStore exposes properties that implement the IQueryable<T> interface,  the BookStore proxy that is generated in the client exposes matching properties of type DataServiceQuery<T>

The BookStore auto generate proxy
   1: public partial class BookStore : global::System.Data.Services.Client.DataServiceContext
   2:  {
   3:      /// <summary>
   4:      /// There are no comments for Books in the schema.
   5:      /// </summary>
   6:      [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Services.Design", "1.0.0")]
   7:      public global::System.Data.Services.Client.DataServiceQuery<Book> Books
   8:      {
   9:          get
  10:          {
  11:              if ((this._Books == null))
  12:              {
  13:                  this._Books = base.CreateQuery<Book>("Books");
  14:              }
  15:              return this._Books;
  16:          }
  17:      }

As we saw in the client side DataServicePorvider implementation, client code can make use of the Books property in order to query for books. 

Running the Application

We will use the same web project in order to run both Silverlight application and WCF data service. To achieve this, the Silverlight application need to be added to the ‘Silverlight Applications’ list of the web project.

image

As a result, after compilation, the Silverlight application will be packed into xap file and appear under the ClientBin directory, and a new aspx file will be added in order to enable running the Silverlight application in debug mode.

image

Additionally, we need to add the BookStoreDataService.svc to the web project, so when we’ll run the web project, it will start the data service.

Consuming the WCF data service via PowerShell Script

Let see how we can query for data from the books store using power shell script.

Open power shell and add the following script:

   1: $webclient = new-object system.net.webclient
   2: $webclient.UseDefaultCredentials = $true
   3: $uri = "http://localhost:16081/BookStoreDataService.svc/Books"
   4: $xml = $webclient.DownloadString($uri)
   5: $xml
   6:  

And the result:

image

Testing the Application

As you may notice, every aspect in the design is affected by the need to provide great testing experience. Starting from using MVVM, through abstracting away the data access layer and providing ways to mock the DataServiceProvider and change the data service URI, to moving the WCF data services logic from the web project, and using the repository pattern in the BookStote data model.  

In the Testing Silverlight 4.0 App with MVVM, MEF and WCF Data Services post we’ll see  how we can take advantage of the those patterns (together with the MEF framework) in order to develop component/multi-component level tests for our application.

I hope you find this article useful, your comments will be appreciated!

The source code can be downloaded from here