Tip 52 – How to re-use Types with the Data Services client
By default when you add a Data Service Service Reference you get automatic code-generation, which produces a strongly typed DataServiceContext and classes for all your ResourceTypes.
You can have a look at this generated code if you ‘show all files’ in your project:
And then expand your Data Service Reference, it’s dependant Reference.datasvcmap and open up the Reference.cs file:
Step 1 – Turn off Code-Gen
If however you want to re-use some existing classes you’ll have to turn this off Code Gen.
Which is pretty easy – select the Reference.datasvcmap file and go to its Properties, and then clear the Custom Tool, which by default says ‘DataServiceClientGenerator’.
Step 2 – Create a strongly typed DataServiceContext:
When we’ve turned off code-gen we also lost the strongly typed DataServiceContext which just makes programming a little more convenient.
Well it’s pretty simple code that you can easily write yourself like this:
public class SampleServiceCtx: DataServiceContext
{
public SampleServiceCtx(Uri serviceRoot) : base(serviceRoot) {
base.ResolveName = ResolveNameFromType;
base.ResolveType = ResolveTypeFromName;
}
protected Type ResolveTypeFromName(string typeName)
{
if (typeName.StartsWith("Sample."))
{
return this.GetType().Assembly.GetType(
typeName.Replace("Sample.", "Tip52.")
false);
}
return null;
}
protected string ResolveNameFromType(Type clientType)
{
if (clientType.Namespace.Equals("Tip52"))
{
return "Sample." + clientType.Name;
}
return null;
}
public DataServiceQuery<Product> Products {
get {
return base.CreateQuery<Product>("Products");
}
}
}
Notice that the Products property simple returns DataServiceQuery<Product> where Product is the type we are trying to re-use.
The key to making all this work is the code that maps from a Data Service Resource typeName to a client-side Type and visa versa.
This mapping is handled by two Functions, which we tell the DataServiceContext about in our constructor. In this example you can see we are simply going from the ‘Tip52’ namespace on the client to the ‘Sample’ namespace on the server.
Step 3 – Try it out:
Once you’ve set up the resolvers you should be able to re-use your existing types with quite easily:
var root = new Uri("https://localhost/Tip52/sample.svc");
var ctx = new SampleServiceCtx(root);
foreach (Product p in ctx.Products)
{
Console.WriteLine("{0} costs {1}", p.Name, p.Price);
p.Price += 0.30M; // Cross the board price increases!
ctx.UpdateObject(p);
}
ctx.SaveChanges();
That’s it. Nifty huh.
Caveats:
This only works if the property names are the same on the client and server, because there is no way to rename properties.
Also because of the way object materialization works in the Astoria client this won't work well if your class has a Reference property with a backing FK property AND the class does automatic fix-up to keep both values consistent.
Comments
- Anonymous
June 02, 2010
You said "AND the class does automatic fix-up to keep both values consistent." Does this mean that Self-Tracking Entities cannot use this solution? - Anonymous
June 09, 2010
The comment has been removed