Microsoft has changed a few things around in the first service pack of the RIA services framework that makes it possible for developers to plug in their own code generators that influence the way various parts of the client code is generated.
Yes that’s right, you can change the way your RIA domain services work. How? Through the use of some very clever T4 templates. This feature gets me excited even more about RIA services and what it offers to developers. In this article I will show you how the code generation in RIA services SP1 works and how you can use T4 templates to manipulate the code generation process.
How does the code generation work
RIA services is as effective as it is because you don’t need to manually add a service agent for a web service. Instead you get the agent for free, because a special MSBuild task generates the client proxies for you. On top of that RIA services also copies validation logic that you wrote on the server to the client. This combination makes it a really powerful framework.
Previously all the goodness that takes care of the code generation was hidden from us non-Microsoft developers. This makes sense when you think about it, because it’s a very ugly part. Loads of code and not really something you want to worry about… Or do you want to worry about that?
Soon after Microsoft released RIA services v1.0 people started asking for a way to modify the code generation process. Developers needed to modify the code generation process, because they sometimes have special requirements that require them to do so. Of course you can do without, but it makes some requirements ridiculously hard to realize.
In SP1 of WCF RIA services Microsoft decided to move things around. They introduced the IDomainServiceClientCodeGenerator interface that you can implement to modify the code generation process. The class can be found inside the Microsoft.ServiceModel.DomainServices.Tools assembly.
All you need to do to actually use the generator is to mark it with the DomainServiceClientCodeGenerator attribute and MSBuild automatically picks it up when building the project (And yes, this even works outside of Visual Studio on your daily buildserver).
Notice that while you can implement your own generator, the implementation provided by Microsoft is still internal and cannot be modified.
Bring in the toolkit
The previous section tells the whole story about how to influence the code generation process in a very short time span. I guess it’s not exactly what you’d expect. But it is what it is, it is undocumented but it works.
You can however improve the experience by downloading the RIA services toolkit from www.silverlight.net/getstarted
This toolkit contains another assembly called Microsoft.ServiceModel.DomainServices.Tools.TextTemplate which contains the necessary components generate code using T4 templates instead of writing code like this:
[DomainServiceClientCodeGenerator("MyCodeGenerator", "C#")] public class PoorMansClientCodeGenerator : IDomainServiceClientCodeGenerator { public PoorMansClientCodeGenerator() { } public string GenerateCode(ICodeGenerationHost codeGenerationHost, IEnumerable<DomainServiceDescription> domainServiceDescriptions, ClientCodeGenerationOptions options) { StringBuilder outputBuilder = new StringBuilder(); // Write your client proxy code here using CodeDom or some other method return outputBuilder.ToString(); } }
The code generator in the RIA services makes building the code above a lot simpler, because the code generation process is divided into several components. The schematic below shows the structure of the code generator in the RIA services toolkit.
To create a code generator you need to create a new class that inherits from ClientCodeGenerator if you want to implement a code generator in VB.NET or from CSharpClientCodeGenerator if you want to implement a C# code generator.
[DomainServiceClientCodeGenerator("MyCodeGenerator", "C#")] public partial class MyCodeGenerator: CSharpClientCodeGenerator { /// <summary> /// Gets the entity generator to use /// </summary> protected override EntityGenerator EntityGenerator { get { return new MyEntityGenerator(); } } // ... Override other properties of the code generator you want to customize }
In this class you decide what you want to change in the process by overriding the respective property for that part. For example if I wanted to change the way entities are generated, I can override the EntityGenerator property and plug in my own entity generator.
It goes even further. If you want to change the entity generator, but only the way constructors are generated, you implement a new EntityGenerator and override the GenerateConstructor method in the new implementation. There is no need to implement it all. Just override the bits you need and leave the rest as is.
Writing a T4 template to generate code
The WCF RIA services toolkit code generator does make things easier to do, but it still requires you to write the ugly bits with StringBuilder and the like. There must be a better way, right?
Yes there is. Microsoft includes a templating engine called T4 text template transformation toolkit (Does anybody else instantly know why this name makes sense?) in Visual Studio. T4 Templates are text files that, when run, result in code being generated. This can be anything from .cshtml files for the Razor viewengine to C# code for your application. There’s even a way to generate code generators themselves by creating a preprocessed template. This allows you to generate code at runtime.
Note: If you want to know more about T4 I suggest you read up on Scott Hanselman’s blog – http://www.hanselman.com/blog/T4TextTemplateTransformationToolkitCodeGenerationBestKeptVisualStudioSecret.aspx
Every part of the code generator you can implement manually using C# can also be implemented using a T4 template. This is best demonstrated using an actual T4 template. For this sample you need to create a new Pre-processed T4 template, you can find this under Project à Add new Item à C# à Pre-processed Text Template.
You will get an empty template that will result in a class with the same name as the template. It will contain a public method called TransformText. When you invoke this method you will get a string back with code that was generated by the template.
To implement the EntityGenerator that is used in the code snippet in the previous section, you need to modify the template so that it looks like what you see below.
<#@ template language="C#" inherits="Microsoft.ServiceModel.DomainServices.Tools.TextTemplate.CSharpGenerators.CSharpEntityGenerator" #> <#@ assembly name="C:Program Files (x86)Microsoft SDKsRIA Servicesv1.0ToolkitLibrariesServerMicrosoft.ServiceModel.DomainServices.Tools.TextTemplate.dll" #> <# base.TransformText(); #>
This template does a couple of things. It is a C# template that inherits from the CSharpEntityGenerator base class. To make the template run you need to tell the engine where it can find the CSharpEntityGenerator base class. It can be found in the Microsoft.ServiceModel.DomainServices.Tools.TextTemplates.dll assembly.
When you run the template you will get a class that looks a bit like the snippet below.
// ------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version: 10.0.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> // ------------------------------------------------------------------------------ namespace RiaServicesTemplatingSample.Web.Generators { using System; #line 1 "D:ProjectsRiaServicesTemplatingSampleRiaServicesTemplatingSample.WebGeneratorsMyEntityGenerator.tt" [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")] public partial class MyEntityGenerator : Microsoft.ServiceModel.DomainServices.Tools.TextTemplate.CSharpGenerators.CSharpEntityGenerator { public override string TransformText() { this.GenerationEnvironment = null; #line 3 "D:ProjectsRiaServicesTemplatingSampleRiaServicesTemplatingSample.WebGeneratorsMyEntityGenerator.tt" base.TransformText(); #line default #line hidden return this.GenerationEnvironment.ToString(); } } #line default #line hidden }
It looks absolutely horrendous, but it is a code generator that you can use. In fact when you compile your project with this template and the code generator in the previous section you will get a working application. MSBuild will be using your code generator instead of the default one. However since you’ve not changed anything it will look as if the build process wasn’t using your code generator.
The code that was placed between <# #> in the original template has become part of the TransformText method. Every piece of text outside of <# #> tags will be converted into WriteLine statements and outputted when you run the template. Because the template only contains a call to the base type it will output no text, but instead call the base class.
It gets more fun when you actually override methods in the entity generator. This is done by expanding the template with method overrides.
<#@ template language="C#" inherits="Microsoft.ServiceModel.DomainServices.Tools.TextTemplate.CSharpGenerators.CSharpEntityGenerator" #> <#@ assembly name="C:Program Files (x86)Microsoft SDKsRIA Servicesv1.0ToolkitLibrariesServerMicrosoft.ServiceModel.DomainServices.Tools.TextTemplate.dll" #> <# base.TransformText(); #> <#+ protected override void GenerateConstructor() { #> // Hello world <#+ } #>
Notice the <#+ #> sections. This tells the tool that transforms the template to place the code in these sections directly into the generated TextTransformation class instead of making it part of the generated TransformText method you saw earlier. The section between the #> tag and the next <#+ tag inside the GenerateConstructor will be outputted when the GenerateConstructor is called.
Now when you run the template and build the project something interesting will happen to the code generated in the client. Instead of a normal constructor you will see a hello world comment in the generated file.
namespace RiaServicesTemplatingSample.Web { [System.Runtime.Serialization.DataContract(Namespace = "http://schemas.datacontract.org/2004/07/RiaServicesTemplatingSample.Web")] public sealed partial class Ingredient : System.ServiceModel.DomainServices.Client.Entity { // Hello world [System.Runtime.Serialization.DataMemberAttribute()] public System.Nullable<System.Decimal> Amount { get { return this._amount; } set {
On its own this is nothing special. But imagine what you can do when you combine this with the fact that you have the DomainServiceDescription available inside your T4 template. It’s like ASP.NET, but instead of generating HTML page you will be generating client code.
Conclusion
The code generation additions in Service Pack 1 for WCF RIA services makes it way easier for developers to extend the code generation process of WCF RIA services based Silverlight applications. For me it makes an already great framework even greater.
Is there a down-side to this? Yep, there always is. Mind you it’s a very small one in my opinion. There’s no intellisense for T4 templates by default, but you can always buy a product for that. Like Clarius T4 editor or similar. I haven’t missed it much since the syntax of T4 is pretty limited, but if you don’t know the .NET framework library or C# very well this can be a bit of a problem and you’re probably better off with a third party editor.
Download the sample and try it out!