For the past couple of weeks I’ve been working on a sample that shows how you can use Prism and .Net RIA Services (I’m going to call it .NRS for short) together. In this post I’m going to walk you through the sample and show how you can structure a Prism & .NRS solution that uses modules as a way to compose applications from both a services and a UI perspective. You can download the source code for the sample application here.
If you’re not familiar with .NRS, it’s framework and related tools that allows you to define a set of entities (or business objects if you prefer) on the server and have those entities made available automatically within your Silverlight client project. You can then use those entities within your Silverlight application where you can create, edit or delete them via the UI, and then save the changes back to the server. .NRS takes care of sending data and updates over the wire so you don’t have to worry about creating and maintaining web services and service references. .NRS also provides support for meta-data driven cross-tier validation and provides common application services, such as user profile, authentication and authorization. See here and here for more details on .NRS and download the July CTP from here.
Quite a few folks have asked me whether Prism and .NRS can work together. The answer, happily, is that Prism and .NRS not only work together they actually complement each other pretty well. When we developed Prism 2.0, we had an item on our backlog to provide guidance on getting data from the server to the client and back again. Unfortunately we didn’t get to that item in Prism 2.0, but .NRS provides support for exactly that scenario. Using Prism and .NRS together is relatively straightforward but there are few issues that you need to be aware of. .NRS is currently available as a CTP so there may be an opportunity for the .NRS folks to address some of these issues before the final release.
The Big Picture
As you know, Prism provides support for building modular client applications in WPF or Silverlight. Prism modules often encapsulate a ‘vertical’ slice of the application’s overall functionality by including all of the view, view model, model and service components required to implement that slice. Such modules are frequently re-usable across multiple applications, allowing you to compose or extend applications from packages of functionality. For example, you might have a financial services application with modules that provide real-time and historical market data, analyst reports, portfolio management, 401K management, options trading, etc. Each of these functional areas can be encapsulated in one or more modules and this allows you to build a single application from an open-ended set of modules, or even a family of applications that each focus on a particular user role or business area.
So how does .NRS fit into this picture?
More and more folks have ‘service enabled’ their back-end systems as part of their move to a service oriented architecture and that has enabled access to a lot of useful data and business processes. Having access to these services is great but it doesn’t really get you very far. The challenge then becomes how to build a compelling and consistent user experience on top of those services. To do that you need a client-side representation of the service that can be composed into a user experience. That representation can of course be a Prism module. The module interacts with the back-end service and provides a user experience for it that can be composed into an application alongside other such modules that represent other back-end services. In effect, Prism & .NRS together provide a way to construct data services and corresponding client-side modules that can be combined into interesting applications.
.NRS allows client-side and server-side projects to be linked together so that they can communicate with each other and share code. .NRS supports a number of configurations but the one we’re most interested in is where the client-side project is a Prism module, and the server side project is a .NRS class library that implements a single data service. In this configuration, the client-side modules are composed into a Shell, and the server-side class libraries are hosted in a web application.
The Sample Application
To show how all of this works we’re going to create a simple order management application which uses two modules – one to display the user’s current orders, and one to display the product catalog. Each module consumes a corresponding data services on the server side. You can imagine that in a real enterprise, the order management system and the product management system could be entirely separate systems. In our simple example, we’re using a single database to represent both (the Northwind database) but we’ll build two independent data services to show how services can be composed.
Before we get started, you’ll need to install Silverlight 3.0, the .Net RIA Services July CTP, and the Northwind sample database. We’ll also use the Prism Quick Start Solution I outlined in a previous post. We’ll start by creating a copy of the Silverlight Quick Start solution and build our application on top of that. When you open the solution, Visual Studio will convert it into a Silverlight 3.0 project. The solution includes two modules called Module1 and Module2. Rename them OrderModule and CatalogModule (you might to change the corresponding assembly name, default namespace and XAP file name too).
The next step is to create the Order and Catalog data services. These are the server-side .NRS class libraries that implement the data services that we’re going to consume in the corresponding Prism modules. Add two new .Net RIA Services Class Library projects to the solution named OrderServices and CatalogServices respectively. You’ll see that this actually creates two projects in a folder – one for the server-side and one for the client-side. We don’t need the client-side project (since we’re going to use the Prism module projects to consume the services) so go ahead and delete the client-side projects OrderServices and CatalogServices, but keep the server-side projects OrderServices.Web and CatalogServices.Web. These server-side projects each come with WorkerRole class (for reasons that escape me) so delete those too.
Before we go any further we need to link the various projects together. We link the two server-side projects to the Web project by simply adding project references. This will cause the services to be ‘hosted’ in the Web project. That was the easy bit. Now we need to associate our client-side module projects with their corresponding server-side service projects. If you look at the module project properties dialog you’ll see there is a .Net RIA Service Link dropdown. This feature lets us link our client-side and server-side projects so that the .NRS tooling can copy and generate the right files. What we want to do is to link our module projects to the corresponding service projects.
Unfortunately, .NRS won’t let us do this easily within Visual Studio. The dropdown only shows the Web project, and not the server-side class library projects that implement the services. That’s because Prism module projects are considered Silverlight application projects (i.e. they produce a XAP file) and not Silverlight class library projects. .NRS only seems to allow Silverlight class library projects to be linked to .NRS server-side class library projects in the project properties dialog. I am not sure why .NRS has this restriction. The simple workaround is to open up the .csproj file in Notepad and add the link manually:
Now when you open up the module project properties dialog you’ll see the correct .NRS link in place:
The next step is to include the right assembly references in the Web and the Prism Module and Shell projects. For the Web project, add references to the .NRS DataAnnotations, DomainServices, and Web.Ria assemblies – Note that the .NRS version of the DataAnnotations assembly is marked as version 188.8.131.52 (don’t know why but I’m sure this is temporary). For the module and shell projects add references to the .NRS DataAnnotations and Windows.Ria assemblies.
Everything should now compile ok. If you view all files in the module projects you‘ll see a new folder called ‘Generated_Code’. This is where .NRS will generate or copy the code that will allow us to interact with the corresponding services.
The next step is to implement the services themselves and the client-side app logic to consume them. This step is pretty straightforward and follows the many tutorials out there on .NRS (like Brad Abrams’ great series on .NRS) so I’m not going to go into the details here (this post is long enough already). I will however summarize the process and call out a few interesting points along the way.
- To implement the Catalog Service, we add an ADO.Net Entity Data Model that pulls in the Northwind Category and Product tables. We then add a Domain Service class that defines the queries on top of that data model – one for all products, one for all categories, and one for a specific product by ID. The Product entity queries include the corresponding Category entities so that the client doesn’t have to make a separate query for category information.
- The Order Service is implemented in the same way except that the data model pulls in the Northwind Order, Order Details and Product tables. Again, the Order entity queries include the Order Details entities so the client doesn’t have to make a separate query for each Order line item. The Order Detail includes the Product name (rather than the entire Product entity).
- As you create the ADO.NET Entity Data Models and the Domain Services for each service, an App.Config file is generated which contains the database connection string. You have to copy this connection string to the Web projects Web.config file. This allows the host web application to provide the connects to the databases that the hosted services projects need. Don’t forget to do this otherwise your services won’t be able to connect to the database and it can be difficult to track down the problem (experience talking here!).
- In addition to including related entities in the data query, you have to provide meta-data to .NRS to tell it to serialize the related entities over to the client. You do this by creating a meta-data class that is associated with the entity class and adding an entry with an [Include] attribute:
- The sample shows how you can use .NRS and the client-side classes that it generates to implement the ViewModel pattern. On the client side, .NRS generates classes for the Product, Category, Order, and Order Details entities and for the Service Context classes that provide access to the services themselves. You’ll see that these classes effectively replace the Model and Services classes that were included in the Prism Quick Start Solution. Each Prism module implements one or two ViewModels which use the service context classes to query data via the server. The entities themselves are considered Models. The ViewModel coordinates the query and selection of entities so that the Views can simply data bind to them.
The sample application is pretty simple. It’s meant to show how to structure a solution that uses Prism and .NRS together. It does not show how to use some of the more advanced features of .NRS, like data updates and application services for authentication and authorization. I’ll cover those in a future post…
The sample also uses some of the cool new Silverlight 3.0 features, such as the PagedCollectionView class and some of the new easing functions (for the compulsory gratuitous animation!) and many of the ‘standard’ Prism features, like the RegionManager, EventAggregator, ModuleCatalog, etc.
.NRS provides a simple way to create data services on top of a database and to expose data entities to the Silverlight client where you can perform CRUD operations on them. Coupled with Prism, it allows you to build flexible applications that compose data services into an rich user experience. To be sure, .NRS isn’t the only way to build and consume data services, and it isn’t suitable for all types of applications, but for some applications it can be very productive and effective.
A couple of words of caution though. While .NRS is technically independent of the data access technology used, it does make it rather too easy to simply expose tables from your database directly to the client. You will have to very careful not to couple your client application to your database by inadvertently exposing your database schema via the services. To avoid this, you should think first and foremost about the data model that you’re going to expose as a service to the client, and then worry about how that maps to persistent storage.
I would also be careful when using the .NRS DomainDataSource component. You can use this to connect your UI directly to the .NRS data services but I think it does mean that you’re implementing, in some sense, application logic directly in the View where it is difficult to test. Personally I’d rather do this kind of thing in the ViewModel where I can unit test it. YMMV though.