|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article tackles a specific challenge you may encounter when using the Text Templating engine shipped with Microsoft's DSL Tools (which by the way can also be completely used without any DSL definition). As soon as you start creating your own text templates, you often like to have additional functionality available, e.g., to navigate the data model (domain model) processed by the template, or some kind of advanced String aValue = <#= ComplexStringManipulation( modelElement ) #>;
In order to be able to call this function, you can add the appropriate extensions to the template file itself in the form of what's called a Class Feature Block. It typically has the form: <#+
public String ComplexStringManipulation( ModelElement modelElement )
{
// Do something special here.
}
#>
This approach has two disadvantages: while it certainly is a quick way to add new functionality and in the case of third-party processors that cannot be modified is actually the only way to go about this, the method starts mixing functionality and presentation features. In advanced scenarios, this will not be well maintainable for well-known reasons. Secondly, the template writer has (at the time of writing) no syntax highlighting or syntax check for this code. So to see whether what really is just plain text will actually even compile, you have to run the template. Finally, it may not be desirable to expose such functionality to clients, for reasons like IP protection or to keep support costs down, as at some point, they will start changing the template. One of the features added to DSL Tools for Visual Studio 2010 will be to support precompiled templates, which will reduce the mentioned disadvantage to some degree. Another approach also mentioned in the DSL Tools book is to create a custom directive processor and extend methods like codeBuffer.AppendLine( @"public String " +
@"ComplexStringManipulation( ModelElement modelElement )" );
codeBuffer.AppendLine( @"{" );
codeBuffer.AppendLine( @" // Do something special here." );
codeBuffer.AppendLine( @"}" );
While this approach removes the disadvantages of exposing and mixing functional code in the template, it is obviously very error prone, will never be nicely highlighted (since the lexer will always treat the statements as pure Well, I am too lazy to go through the exercise of listing the CodeDOM statements required to define the function above and do at least some minimal The next section shows you yet another option which is built into DSL Tools, but does not seem to have been mentioned much so far. Injecting your Custom ClassThis is actually pretty simple: instead of writing plain text that hopefully compiles later on or to spend hours emitting code using CodeDOM, you just create your own regular C# (or any other .NET language) class, which:
For our sample above, this would look like so: namespace MyNamespace
{
public abstract class MyGtcBase : TextTransformation
{
public String ComplexStringManipulation( ModelElement modelElement )
{
// Do something special here.
}
}
}
To be able to take advantage of methods and properties defined in the new class, you can use the <#@ template inherits="MyNamespace.MyGtcBase" #>
The latter directive will result in the fact that your base class is now injected into the inheritance line of the generated template class, i.e., all The approach shown above gives you full IDE support during development of extensions to the generated template class. It also keeps this code away from the template, and protects the code from being looked at or being changed. The extra effort of creating the class is minimal compared to just using a mixed-in class feature control block. We are using this approach in production generators. Update: There is an issue with this approach as Oleg Sych explains in his blog: If used inside Visual Studio, the IDE seems to lock the assembly containing the base class for up to 25 times for performance reasons. One way around this is to run another template not referencing the base class. In our project we are actually using a different transformation host (not Visual Studio), so the problem does not occur either. Using the CodeTo see the sample solution in action, do the following:
As a result, you will see the output text file (shown as code-behind in the project tree) has changed accordingly. I hope with this sample, the approach becomes somewhat easier to use. History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||