RIA Services (new home page) is a collection of tools and libraries for making Rich Internet Applications, especially line of business applications, easier to develop. Brad Abrams did a great presentation of RIA Services at MIX 2009 that touches on querying, validation, authentication, and how to share logic between the server and client sides. Brad also has a huge series of articles (26 as I write this) on using Silverlight and RIA Services to build a realistic application.
I love the concept of RIA Services. Brad and his team have done a fantastic job of identifying the critical issues for LOB systems and have the right idea to simplify those common data access tasks through the whole pipeline from database to UI controls, using libraries, Visual Studio tooling, or whatever it takes to get the job done.
So before I lay down some heavy criticism of RIA Services, take into consideration that it’s still a CTP and that my scenario pushes the boundaries of what was likely conceived of for this product, at least for such an early stage.
Shared Data Model with WPF & Silverlight Clients
The cause of so much of my grief with RIA Services has been my need to share a data model, and access to a shared database, across WPF as well as Silverlight client applications. Within the constraints of this situation, I keep running into problem after problem while trying to use RIA Services productively.
The intuitive thing to do is: define a single data model project that compiles to a single assembly, and then reference that in my Silverlight and non-Silverlight projects. This would be a 100% full-fidelity shared data model. As long as the code I wrote was a subset of both Silverlight and normal .NET Frameworks (an intersection), we could share identical types and write complex validation and model manipulation logic, all without having to constrain ourselves to work within the limitations of a convoluted code generation scheme. Back when I wrote Compact Framework applications, I did this with great success despite the platform gap, and I didn’t have anything like RIA Services to help.
Part of the problem arises because Silverlight assemblies are incompatible with non-Silverlight assemblies. A lot of what RIA Services is doing is trying to find a way around this limitation: picking up attributes and code files from one project and inserting that code into the Silverlight project with a build action. This Visual Studio “magic” has been criticized for its weakness in dealing with multiple-solution systems where Visual Studio can’t update the client because it’s not loaded, and I’ve heard there’s work being done to address this, but for my current needs, this magic aspect of it isn’t a problem. The specifics of how it works, however, are.
Different Data Access APIs
Accessing entities requires a different API in Silverlight via RiaContextBase versus ObjectContext elsewhere. Complex logic in the model (for validation and other actions against the model) requires access to other entities and therefore access to the current object context, but the context APIs for Silverlight and WPF are very different. Part of this has to do with Silverlight’s inability to make synchronous calls to the server.
In significantly large systems that I build, I use validation logic such as “this entity is valid if it’s pointing to an entity of a different type that contains a PropertyX value of Y”. One of my tables stores a tree of data, so I have methods for loading entire subtrees and ensuring that no circular references exist. For these kinds of tasks, I need access to the data context in basic validation methods. When I delete nodes from a tree, I need to delete child nodes, so update logic is part of the model that needs to be the same in every client. I don’t want to define that multiple times for multiple clients. I like to program very DRY. In other words, I find myself in need of a shared model.
RIA Services doesn’t provide anything like type equivalence for a shared model, however. Data model classes in Silverlight inherit from Entity, but EntityObject in WPF. In the RIA Services domain context, we RaiseDataMemberChanged, but in a normal EF object context, we need to ReportPropertyChanged. In WPF, I can call MyEntity.Load(MergeOption.PreserveChanges), but in Silverlight there’s no Load method on the entity and no MergeOption enum. In WPF I can query against context.SomeEntitySet, but in Silverlight you would query against context.GetSomeEntitySetQuery() and then execute the query with another method call.
This chasm of disparity makes all but the simplest shared model logic impractical and frustrating. The code generation technique, though good in principle, keeps getting in the way. For example, I have both parameterless and parameterized constructors in my entity classes. This works great in my WPF client, but when this code is synchronized to my Silverlight client, I get an error because the Silverlight-side entity class is generated in two parts: in the hidden partial class, a parameterless constructor is generated which calls partial method OnCreated; and in the visible partial class, the constructor method I defined on the server is dumped into another file, so I have duplicate constructors. If I remove the parameterless constructor from the server side, I get an error because my entity class requires a parameterless constructor (and defining a non-default constructor effectively removes the default one from the resulting type unless it’s explicitly defined). I thought I could define the partial method OnCreated and put my construction logic in there, but the partial method is only defined on the client side. That means sharing construction logic consists of copying and pasting the OnCreated method across the various clients—far from an ideal solution.
Entity Data Model Required to be in Web Project
Another strategy I attempted was to define the .edmx file and my partial class extensions in a class library, and then reference that from the web project. I could define the LinqToEntitiesDomainService<MyDataContext>, but sharing entity class code (by generating code in the Silverlight project) isn’t possible unless the .edmx file and partial class extensions are defined in the web project itself. This would mean that my WPF client would have to reference a web project for data access, which by itself seems wrong. (Or making a copy of the data model, which is worse.) It would be better for the WPF client to talk to the same domain service as the Silverlight client, but RIA Services doesn’t give you an option to link that web project to a non-Silverlight project, so again I ran into a brick wall.
So Don’t Do That
The kind of advice I’m getting for this is, “so don’t do that”. In other words, don’t write complex validation logic in the model or otherwise try to access the data context; don’t write parameterized constructors; don’t aim for 100% type fidelity across all endpoints of a system; don’t try to share data models with Silverlight and non-Silverlight projects, etc. But I see the potential for RIA Services, so I have to push for these things unless I hear really convincing arguments against them (or compelling alternatives).
The fact that there are different data contexts and data item definitions within those contexts imposes a burden on the developer to use different techniques for each environment, and creates challenges for centralizing data model logic and reusing equivalent logic across different kinds of clients. My gut feeling is that RIA Services in its current form has some fundamental design flaws that will need to be addressed, taking into consideration systems with a mix of Silverlight, WPF, and other clients, before it becomes a truly robust data access platform.