In order to share code, such as Data Transfer Objects (DTOs), there are a number of ways to address the situation. First, I’ll address a way that only works in C# and then I’ll describe a method that works with C# and VB.NET. WCF RIA Services has a goal of sharing code between the client and server, but this post is for those situations when WCF RIA Services isn’t used.
The scenario being addressed has a Silverlight application and a ASP.NET web application. The issue is sharing objects between them. Using Windows Communication Foundation (WCF) for services that the Silverlight application will call, objects are exposed through the service contract, but I would like to use the Message Gateway pattern to abstract away the service calls within the client.
Using a single class library to share code (C# only)
If you create a new project using the template for Silverlight Class Library, you will be able to reference that class library in both the Silverlight application and the ASP.NET web application. From within the service layer in the web application, you can create and use any objects that cross the application boundary in the Silverlight class library.
When creating a service reference in the Silverlight application, point it at the WCF service you have just created and make sure to select “Reuse All Application Assemblies” or select the individual namespaces that you have created within the Silverlight class library. You will then need to reference the Silverlight class library assembly in the Silverlight application.
Using two class libraries to share code (C# and VB.NET)
If you’re using VB.NET the method above does not work, as the compiler will complain about different runtime versions. The cause of this may not be the compiler itself, but rather something with the VB.NET runtime. Regardless, the next method described will work, but it takes a little more work.
Given the Silverlight application and the ASP.NET web application, you will need to create two more projects. One will be a Silverlight Class Library and the other will be a traditional .NET Class Library. You will create the objects within only one of the class libraries. You can then use the “Add As link” option to include the file within the other class library. What this does is provide two assemblies with the same code, but compiled for different runtime versions. You are not technically duplicating code in the copy and paste sense, but you are producing duplicate copies of code from a single source.
I like to use the Model-View-ViewModel (MVVM) pattern in Silverlight development and implementing it with these approaches should be addressed.
The first method that works only in C# does not work as well with MVVM because implementing INotifyPropertyChanged for Silverlight and Windows Presentation Foundation (WPF) causes namespace clashes. The non-Silverlight class library requires using the WPF definition of INotifyPropertyChanged. The different runtime environments do not allow this to compile correctly.
When using the second method, the two resulting projects are compiled separately for the different runtimes and therefore work in both Silverlight and WPF.
A third scenario works when using MVVM and the first method, but the implementation of INotifyPropertyChanged needs to be moved from the Model to the ViewModel. This works well for every scenario I’ve seen except when using an ObservableCollection bound to an editable DataGrid. This is because the INotifyPropertyChanged events are fired when the list data structure changes, not when an item within the list changes. Since the items within the list, which are coming directly from the Model, don’t implement INotifyPropertyChanged, it creates a dilemma when handling row saves. Specifically, reloading the whole grid after a single row save is not an ideal user experience (UX). When I’ve come across this problem, I’ve used the second method. There may be some way around this with creating a new List type that implements INotifyPropertyChanged and INotifyCollectionChanged in which it specifically handles the special case. That seems like reinventing the wheel and resulting from a snowball effect of taking the quick way of sharing data, which in short means the code smells!
Keeping the models separate
I should outline a more proper approach of using Silverlight, Services and MVVM. Let us start with the WCF service layer and below. This area of the overall application should have full access to the domain objects, which will be referred to as Model-A. Model-A would typically be created using ADO.NET Entity Framework, LINQ to SQL, or whatever Object-Relational Mapping (ORM) tool you prefer. As part of handling service calls, your service layer would also perform mapping to DTOs, which will be referred to as Model-B. The DTOs, by definition, are the objects that are transferred over the application boundary. Now for the client-side (Silverlight) that will consume the services. Silverlight will have service references and will receive DTOs as part of the communication with the services. If the message gateway pattern is used, that would be an ideal place to then convert the DTOs to a model that can be used in the MVVM pattern. This client-side only model will be referred to as Model-C. Model-C is the place to implement INotifyPropertyChanged so that you can take advantage of the advanced XAML binding. Using these three models, you are clearly separating concerns and keeping each model pertinent to its application module. Specifically, you are not letting user interface code influence anything outside of the UI. You’re not restricting consumers of the services by adding UI or persistence code to the DTOs and you’re keeping the persistence code in the same place that queries are performed.
What about WCF RIA Services
I mentioned that these approaches are for when WCF RIA Services aren’t being used, but what does WCF RIA Services do anyway? Currently, in short, it creates a client-side set of classes based on the server-side set of classes and then adds them to the Silverlight project during compilation. It does offer some great things, so consider using it before trying to reinvent the wheel. At the time of creating this post, the WCF RIA Services project is in beta for Visual Studio 2008 and preview for Visual Studio 2010, so it’s subject to change.
If you have found other approaches or complications with the methods above, please let me know!