WCF RIA Services and a guide to use DTO/”Presentation Model”
NOTE: Examples in this blog post is based on the VS 2010 Beta 2, WCF RIA Services PDC Beta, some changes to the WCF RIA Services can happen until RTM.
I have seen several questions and request about how to use Data Transfer Objects (DTO), or a ”Presentation Model” (Model created for presentation purpose) with WCF RIA Services. In this post I will write something about it.
Why use DTO/”Presentation Model”?
Why use DTO/”Presentation Model” at all? Well, domain entities aren’t design with presentation in mind; they are design to solve business problems. Because of that a domain entity is not created with presentation in mind and shouldn’t be passed to the View. If we do it, we will end up with a lot of logic in our view only to make sure our view will show the domain entities in the way we wants, and it can also ends up that we send more data than a view is needed. So with a DTO/”Presentation Model” we can create a new “model” with is designed with presentation in mind, and also will only contains the data the View needs.
My Windows to my logic
If we have a domain model in place or a simple n-tier architecture with a business logic layer and data access layer, we need to add a Service layer on top of them, and this layer is designed and created for our client in a Rich Internet Application only. The Service Layer will be our window to our logic. It’s the Service layer responsibility to communicate to our domain model or business logic, it also has the responsibility to only pass the data a client needs. This kind of data passed to the client is a DTO/”Presentation Model”. The Service layer knows about the domain model and also the DTO/”Presentation Model”. The domain model doesn’t know about the Service layer. The client/view or should I say “presentation layer” will only know about the existents of the Service layers and its services it will use, and also the DTO/”Presentation Model”, it will not know about the domain model.
The above figure shows how I often design my RIA. But and a big BUT, I select the design which is best suited for the application I’m building. If you take a look at the figure above, you can see that the “DTO/PM” is also known to the View, this is not always the case, which is why there is a box which will stop at the ViewModel. I have seen several articles and blog post where they say they use a ViewModel, but what they do is to pass the DTO directly to the View for rendering. Is that wrong? I will not say it is, instead say “It depends”. If our DTO/”Presentation Model” is well designed for a specific View, it is ok to pass it along through a ViewModel, but if it isn’t, it’s not ok. The ViewModel in this case is similar to Martin Fowlers Presentation Model pattern. Some developers still uses code-behind and no ViewModel, so in that case the View will use the DTO.
Note: In this post I will not use the ViewModel, I will just simply use code-behind to call the Service layer, this post is only about how we can use DTO/”Presentation Model” with WCF RIA Services. If you want to read more about RIA Architecture etc, you can check our the following blog post: http://weblogs.asp.net/fredriknormen/archive/2009/04/19/ria-architecture-with-silverlight-in-mind.aspx
Creating a Service Layer with WCF RIA Services using DTO/”Presentation Model”
Before creating the Service Layer, we will take a look at the Entity I have in my domain model.
The following is the View which should display the Customer:
As you can see the View and the Customer entity are very different. If we pass our Customer to the View, we need to add logic which will combine the FirstName and LastName property, and also add logic which will check the checkbox Is Adult, if Age >= 18 (In Sweden you are an adult it you are 18 years old ). The View is not interested in the Address, Phone property, so why pass it to the View? So what we can do is to create a DTO/”Presentation Model” for the View. We can add this model in a separate class library or to the Web projects which will host our Silverlight application. In a small application I often only create a folder in my Web project called “PresentationModel” and put my DTO/”Presentation Model” classes to that folder. The reason I do that is because the DTO/”Presentation Model” I will create is only for my Silverlight application, not for other clients. Here is my DTO/”Presentation Model”:
public partial class CustomerPM { [key] public int CustomerID { get; set; } public string FullName { get; set; } public int Age { get; set; } }
Note: I will make the DTO/”Presentation Model” partial, the reason I do that is to make it possible to use the WCR RIA Services .shared feature.
The DTO/”Presentation Model” should only pass the data the view is needed, nothing more. The CustomerID is marked with the KeyAttribute, and we must at least mark one property to be a identifier of the object. WCF RIA Service’s DomainContext has an Identity Map, and it will cache objects based on the specified key. The View will check a CheckBox if the Customer is an adult. We can pass the Age along to the View and use a ViewModel to add the logic to know if the Customer is and adult or not, but in this case we will not use a ViewModel (I do prefer a ViewModel), we instead turn the DTO into a “Presentation Model” instead. So we will use the .shared feature to add an IsAdult property. A DTO will not have any kind of logic, it will only be used to pass data over the wire, so if we add logic to a DTO, it will instead be something else, and in this case I will say it will be a “Presentation Model”, a model used to simply the presentation of an domain entity. Here is the .shared code for the CustomerPM:
CustomerPM.shared.cs:
public partial class CustomerPM { public bool IsAdult { get { return this.Age >= 18; } } }
Now when the “Presentation Model” is created we need to create our Service Layer. The Service Layer in this case is the WCF RIA Services DomainSerivce, so here is the DomainService which will use the “Presentation Model”:
[EnableClientAccess()] public class CustomerService : DomainService { private ICustomerRepository _customerRepository; public CustomerService() { _customerRepository = new CustomerRepository(); } public CustomerService(ICustomerRepository customerRepository) { _customerRepository = customerRepository; } public CustomerPM GetCustomer(int customerID) { var customer = _customerRepository.GetCustomerByID(customerID) return new CustomerPM() { CustomerID = customer.CustomerID, FullName = customer.FirstName + " " + customer.LastName, Age = customer.Age }; } }
As you can see a WCF RIA Services query method can return a simple object, in this case the CustomerPM. The Query method will make a call to our domain model to get the Customer entity.
Note: In the current WCR RIA Services PDC beta for VS 2008, you can’t return a single entity, but it can be done in the VS 2010 version.
The CustomerRepository’s GetCustomerByID method can use Entity Framework, Linq To SQL, nHibernate or normal ADO.NET etc to fill a Customer entities with data from a data source.
By using Object Initializer we can simply map our domain entity to our “Presentation Model”. Normally I will do some refactoring here, and create a MapCustomerToCustomerPM method, which will handle the mapping. As you can see the CustomerService will take a ICustomerRepository as an argument to the constructor, this constructor can be used when we write unit test to test the DomainService. The default constructor will create a “real” instance of the CustomerRepository. Normally I would use a Dependency Injection framework, like Unity or StructueMap etc, can also use MEF (which is not a “pure” DI framework) to inject a ICustomerRepostiroy when the DomainService is created, to make that possible when using WCF RIA Services, we can for example create our own IDomainServiceFactory, I prefer my own IDomainServiceFactory instead, I will post a blog post about that later.
Now when our DomainService is in place we can now use it from our Silverlight application.
Calling our DomainService from Silverlight
WCF RIA Services will generate classes for us on the client-side, it will create a state-full context class for us (DomainContext), which will have Dirty tracking, Unit or Work and a Identity Map, we will use that class to load our entities into the context. The name of the DomainContext will be CustomerContext. WCR RIA Services will take the name of the DomainService when it creates the DomainContext, if the name has the suffix “Service”, it will rename it to “Context” on the client-side, if not it will keep the name of the DomainService. The following code will make an asynchronous call to our DomainService and get a specific customer and set the LayoutRoot’s DataContext to the CustomerPM and use data-bounding:
var customerContext = new CustomerContext(); customerContext.Load<CustomerPM>(customerContext.GetCustomerByIDQuery(10), loadOperation => { LayoutRoot.DataContext = loadOperation.Entities.First(); }, null);
Here is the XAML:
<UserControl x:Class="SilverlightApplication1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Grid x:Name="LayoutRoot"> <StackPanel> <TextBox Text="{Binding FullName}"></TextBox> <CheckBox IsChecked="{Binding IsAdult}"></CheckBox> </StackPanel> </Grid> </UserControl>
Summary
As you may notice in this blog post, it’s quite easy to use DTP/”Presentation Model”, if we want to add validations to our DTO/”Presentation Model”, we can simply do that with annotation.
If you want to read more about DTO/”Presentation Model” you can take a look at the following posts:
If you want to know when I have published a new blog post, you can follow me on twitter: http://www.twitter.com/fredrikn