One challenge with ADO.NET Data Services is that it has trouble understanding what’s going on if you add custom properties to your entities on the client side. This post will explain using converters to overcome that challenge when using Silverlight. Or WPF, I suppose.
Background
When you add a reference to an ADO.NET Data Service, Visual Studio generates a set of client classes that represent the entities on the server. These classes are tantalizing marked as partial, which could lead you to believe that you are welcome to add additional client-only properties to these classes.
1: [global::System.Data.Services.Common.DataServiceKeyAttribute("Id")]
2: public partial class Employee
3: {
4: }
However, if you extend the partial class to add a property, ADO.NET Data Services will be grumpy when you try to save your object since it won’t know what to do with the extra property.
Let’s say that we want to add an extra property to our Employee class called “HasCornerOffice” that returns “true” if the employee has “VP” in their title. Something like this.
1: public partial class Employee
2: {
3: public bool HasCornerOffice
4: {
5: get { return this.Title.StartsWith("VP"); }
6: }
7: }
When you try to save an instance of an employee back to the database, you’ll get an error along the lines of “Error processing request stream. The property name ‘HasCornerOffice’ specified for type ‘Employee’ is not valid.” ADO.NET Data Services has no idea what the property is all about and is letting us know.
Some Solutions
There are a few ways around this.
You could use a brand-new client-only class that can be mapped to and from Employee. Adding this layer of abstraction could make a lot of sense and add some insulation between your client and server that could come in handy later. Not a bad way to go for the trade-off of writing and maintaining the mapping code to back and forth between the client and server objects.
You could also customize the way that objects are serialized by digging into the bowels of the data context and intercepting the ATOM request before it is send to the server. Not sure what this would do if you were using JSON, but this blog post has the details for wiring it up with an ATOM request.
Using Convertors
An alternate quick and easy solution is to leave the client-side entities as-is and use a converter to transform our Employee data into the data that we want to display. This has the advantage of being straightforward and low-impact, but is maybe not as flexible or extensible long-term as the two solutions above.
Using the same Employee example as above, our Converter would look like this.
1: public class CornerOfficeConverter : IValueConverter
2: {
3: public object Convert(
4: object value,
5: Type targetType,
6: object parameter,
7: CultureInfo culture)
8: {
9: if (string.IsNullOrEmpty(value as string))
10: {
11: return false;
12: }
13:
14: return value.ToString().StartsWith("VP");
15: }
16: }
And we could bind to it like this.
1: <TextBlock
2: Text="{Binding Title,
3: Converter={StaticResource TitleConverter}}" />
Conclusion
Hopefully, this shortcoming in ADO.NET Data Services will be resolved in the framework itself in the next version or so, but in the meantime, those are some ideas of how to work around the problem.