I recently worked on building a Silverlight LOB application and that became the perfect opportunity to try out RIA Services along with Code First.
What does RIA Services do? Well, if you have built a Silverlight application, you will know that you have to write server side code and client side code separately – and that the server side exposes certain web services, which will be consumed by code written on the client side. A common pattern used is MVVM, where the server side logic uses Models, whereas the client side uses ViewModel(s) to wrap the service calls and provide a data context for binding to the Views. Views (as name suggests) takes care of the UI screens.
What RIA services does is it simplifies this by replicating all your server side models on the client side by generating code on the client (this code is generated everytime the silverlight project builds). Since the generated clientside view models have one-one mapping with the server side models, it also wraps the service calls necessary for CRUD operations, and has the appropriate notifications for updating the views. RIA Services takes care of the serialization and the deserialization of model data, so you code only against strongly typed classes. You can use the view-models directly in your views, as well as instantiate domain service calls to fetch data from the server. The server has a service context which exposes all the appropriate CRUD methods for each entity, including any validation or filtering logic if necessary (this is fully controlled by the developer).
This does deliver on its promise of building a Rich client CRUD applications painlessly, but it certainly has it’s own gotchas. Knowing them beforehand will certainly help.
- The current version is dependent on an older version of Entity Framework (4.1 vs the current 4.2). This will probably continue, considering that RIA services team works somewhat independently of the EF team. The worst thing about this, is that if you use Nuget and install EntityFramework package first, and then try to install RIAServices.EntityFramework, it will give you an exception (since it tries to install EF 4.1 whereas you already have a newer version). The solution is to not install EF, just install RIA services and Nuget will get the appropriate version of EF for you.
- Entity Serialization rules will take a bit of pain to understand and get it right. There will be several cases where the data is fetched on the server side by EF, but not serialized and received on the client side and you’ll be left wondering why. Here are some of the rules you have to follow (there are some good reasons for why it works this way) –
- Collections too need an [Include] attribute to be serialized and sent to the client. Along with this, for EF itself you need to remember to use the Include() LINQ expression to ensure that the data is fetched on the server side even before serialization. (you need LINQ expression to fetch data from the DB, the notation to tell RiaServices to serialize the collection property.)
- You explicitly need to mention the FKeys for reference properties. For eg, if you have a property of Customer type named Cust, then you also need an id property named CustID. Although you don’t have to worry about filling this manually, you can just use the reference properties and the dbcontext will take care of updating the id.
- Methods don’t get copied from server side to client side code. If you want certain code to be shared, you should put those files in the “shared” folder, either the entire class or at least as partial classes (the parts that you need on client side)
- You can define partial classes on the client side to extend the capabilities of view models generated by RIA services
- Attributes, attributes, attributes – you can use attributes for defining display order/other display properties, validations, control serialization, etc. on your serverside models. This does seem like polluting the model code, but it is much more efficient than writing this logic separately. For new-comers though, it may make things difficult to understand (they will just keep searching where the textboxes in a dataform are coming from, without realizing they have to look at the server model attributes).
- Calculated properties defined on the server side will just go as normal properties on the client side – for instance if you have film.Height and film.Width, and film.Area is a calculated property with only a getter, still on the client side a property Area is created with normal getter/setter. What happens is the Area property is fetched from the server and sent over to the client instead of being generated on the client side. So if you update height on the client side, it won’t immediately affect Area on the server side. Also since Area has a setter, you can by mistake set the area to a new value, but this will throw an exception on submit changes since your server model does not have a setter. Possible solutions are either put calculated properties in shared code, or if you don’t want to use this on the client side just use the [Exclude] property.
- The code gets generated every time you build the client project, not when you build the server project. This can be irritating if you are looking for some properties in intellisense that you just added on the server side but are not yet available (because you din’t build the client side after making your changes).
- You are going to write a decent amount of code-behind. This is because dealing with logic in the view model is painful, since you have a one-to-one mapping between your view models and models (unlike well designed MVVM code).
- IQueryable and IEnumerable are both allowed as return types for the server Get[Entity] methods. IQueryable works a bit like magic and is more preferable – you can put filters directly on the server side based on entity properties (even though you haven’t defined any parameters for the server side method) and this will fetch only the required data. This is very useful to avoid having to write too many server side methods or keeping unnecessary nullable parameters
- Comboboxes are a pain to deal with – you cannot use domaindatasource to bind to a combobox. Luckily comboboxextensions package should help.
- If you fill the reference property of an entity with another entity and send it back from the client to the server for an update it can give you problems – for instance the server dbcontext will think that the referenced entity is a new entity and will try to insert that into the database, whereas being an existing entity the id will already be present. This will cause an exception. Solution – on the server side, just set all the reference entities to null and let the corresponding fk IDs to handle the updates.
- If you have any models that are not related to any database tables (for instance say report model) then good luck getting them on the clientside! Entities not mapped to the database are not considered by Ria services. This is really wierd, and I ended up with empty tables just to get the corresponding viewmodels generated. This wierdness extends to reference properties as well – if for some reason you don’t want them to be mapped to the database, then they won’t be copied to the client code.
Let me know how your experience has been so far. Did you find any more gotchas?