Code generation isn’t new in Visual Studio. Nearly every visual design surface, from Web Forms to Windows Forms to typed DataSet designers, has transformed shapes into code using code generation behind the scenes. The key phrase is behind the scenes. As developers, we did not always have the control we needed over the code generation process. What is new in Visual Studio is how more frameworks are using T4 Templates for code generation. The frameworks integrating with T4 can give developers complete control over the code generation process. The Entity Framework is one such framework.
This paper will discuss how the Entity Framework uses T4 Templates to generate custom code from an ADO.NET Entity Data Model (EDM), and assumes the reader is already familiar with the Entity Framework’s API and designer. We’ll begin with an overview of the T4 technology and see how the technology works. We’ll also look at how we can programmatically consume metadata from an EDM. In the end, we’ll combine these two pieces of knowledge to customize a T4 template.
What Are T4 Templates?
T4 is a general-purpose templating engine you can use to generate C# code, Visual Basic code, XML, HTML, or text of any kind. The first step in using T4 is to create a template file, which is a mix of literal text, code, and processing directives. The T4 template processor will transform this file into a text file of the desired type by executing the code and processing directives inside. As an example, you can right-click on any project in Visual Studio 2010 and add a new item of type Text Template.Notice how a T4 template file uses an extension of .tt by default. You can add templates to any type of Visual Studio project – console applications, class libraries, ASP.NET, or WPF. If you look at the properties on a newly added template file in Solution Explorer, you’ll see the Custom Tool property is set to TextTemplatingFileGenerator. This custom tool is the template processor. The processor transforms the template file into a textual output file. In the following figure, we’ve added a template named Simple.tt.
The simplest possible content for Simple.tt might look like the following.
- <#@ output extension=".txt" #>
-
- This content was generated from a template
- This content was generated from a template
Transformation
There are a number of options available for template processing. One option is to process a template while your application is running. Runtime execution of a template means you could use T4 to generate form letters or HTML output. However, in this example we are using a more common T4 strategy and allowing the custom tool (TextTemplatingFileGenerator) to transform the template before the project even compiles.Looking at the template in the Solution Explorer window, you’ll see Simple.tt is a tree node you can expand to reveal a file “behind” the template.
In this example, the output file is Simple.txt. By default, the generated file will have the same name as the template, but with a different extension (as specified in a processing directive).
If we could only use literal text inside of a template, then T4 would be a wholly unremarkable technology. Fortunately, we can also place executable code inside a template. Let’s change Simple.tt to use the following content:
- <#@ template language="C#" #>
- <#@ output extension=".txt" #>
-
- This content was generated from a template
- in the year <#= DateTime.Now.Year.ToString() #>
- This content was generated from a template
- in the year 2010
What Can T4 Templates Do For Me?
By combining literal text, imperative code, and processing directives, you can transform data in your environment into buildable artifacts for your project. For example, inside a template you might write some C# or Visual Basic code to call a web service or open an Excel spreadsheet. You can use the information you retrieve from those data sources to generate code for business rules, data validation logic, or data transfer objects. The generated code is available when you compile your application.As an example, let’s imagine we want to work with a SQL Server database. Instead of allowing our application code to use low-level ADO.NET abstractions from System.Data.SqlClient (like DataTable and SqlDataReader), we want to use custom C# class definitions for each table in a database. These classes will hide the ADO.NET details needed to access the table. We can create those classes by hand, or we can use a T4 template to generate the class definitions directly from the database schema.
- <#@ template language="C#4.0" #>
- <#@ assembly name="System.Data" #>
- <#@ import namespace="System.Data.SqlClient" #>
- <#@ import namespace="System.Collections.Generic" #>
- <#@ output extension=".cs" #>
-
- <#
- foreach(var name in GetTableNames())
- {
- #>
- public class <#= name #>
- {
-
- }
-
- <#
- }
- #>
- <#+
- IEnumerable<string> GetTableNames()
- {
- var connectionString =
- @"Data Source=.;Initial Catalog=movies;Integrated Security=True";
-
- var commandText = "select table_name as TableName
- from INFORMATION_SCHEMA.Tables";
-
- using(var connection = new SqlConnection(connectionString))
- {
- connection.Open();
- using(var command = new SqlCommand(commandText, connection))
- using(var reader = command.ExecuteReader())
- {
- while (reader.Read())
- {
- yield return reader["TableName"] as string;
- }
- }
- }
- }
-
- #>
- <#
- foreach(var name in GetTableNames())
- {
- #>
- public class <#= name #>
- {
-
- }
-
- <#
- }
- #>
- public class movies
- {
-
- }
-
- public class reviews
- {
-
- }
After we regenerate code we might need to change the code in our application that uses the old class definition, but the compiler will help us locate these cases. It’s easier to find breaking changes when we generate strongly-typed code for data access. Compare this approach to using DataTables and DataReaders and specifying names using string literals. The compiler is no help in trying to track down breaking changes.
At this point, we could continue working on the template to generate properties for each column in a table, and perform aesthetic work like uppercasing the first letter of each table name (since C# class definitions should begin with a capital letter). However, with the ADO.NET Entity Framework we do not need to operate at this low level, yet we can still use T4 Templates to have complete control over the code we’ll use for data access.
Before we can take a closer look using T4 Templates together with the Entity Framework, let’s drill into specific capabilities of T4 Templates.
Working with T4 Templates
Every developer should know a few basics before working with T4. This includes how to add assembly references, and use control blocks and directives. We’ll start by looking at template directives.Directives
Directives provide instructions and information to the transformation engine. We used two simple directives in the simple template we wrote earlier.
- <#@ template language="C#" #>
- <#@ output extension=".txt" #>
The output directive gives the processor a file extension for the generated file. A text template generally outputs a single file named the same as the template, but with the extension specified in the output directive. Later, we’ll see templates generating multiple output files.
Additional directives you’ll frequently see are the assembly and import directives.
- <#@ assembly name="System.Data" #>
- <#@ import namespace="System.Data.SqlClient" #>
Once you’ve referenced an assembly you’ll probably want to use the import directive. The import directive is just like a using statement in C# - the directive brings a namespace into scope so you do not need to use fully qualified type names.
Finally, the include directive allows you to include the contents of another file into a template.
- <#@ include file="EF.Utility.CS.ttinclude"#>
Control Blocks
After directives, there are two types of content you’ll find in a template. The first is the literal text the template will output directly. The second type of content is code inside of control blocks. There are three types of control blocks in T4. The first type is the expression control block, which we delimit with <#= and #> tokens.
- This content was generated from a template
- in the year <#= DateTime.Now.Year #>
Standard control blocks are not required to produce a value, and you delimit them with <# and #>.
- <#
- var sum = 0;
- for(var i = 0; i < 10; i++)
- {
- sum += i;
- }
- #>
This does not compile:
- <#
- int SumNumbers(params int[] numbers)
- {
- return numbers.Sum();
- }
- #>
However, a third type of control block does allow you to create reusable methods. The class feature control block uses <#+ and #> delimiters. The following code will work because the SumNumbers method exists inside a class feature block.
- <#@ template language="C#" #>
- <#@ output extension=".txt" #>
- <#@ assembly name="System.Core.dll" #>
- <#@ import namespace="System.Linq" #>
-
- Results:
- <#= SumNumbers(1,2,3) #>
- <#= SumNumbers(4,5,6,7) #>
-
- <#+
- int SumNumbers(params int[] numbers)
- {
- return numbers.Sum();
- }
- #>
- public partial class SimpleTemplate : TextTransformation
- {
- public virtual string TransformText()
- {
- GenerationEnvironment = null;
- Write("\r\nResults:\r\n");
- Write(SumNumbers(1,2,3).ToString());
- Write("\r\n");
- Write(SumNumbers(4,5,6,7).ToString());
- this.Write("\r\n\r\n");
- return GenerationEnvironment.ToString();
- }
-
- int SumNumbers(params int[] numbers)
- {
- return numbers.Sum();
- }
- }
- <#
- Write(SumNumbers(1,2,3).ToString());
- Write(SumNumbers(4,5,6,7).ToString());
- #>
-
- <#+
- int SumNumbers(params int[] numbers)
- {
- return numbers.Sum();
- }
- #>
Editing and Debugging Tips
Since T4 templates are text files you can edit them with any text editor, including Visual Studio. However, there are some tools to improve the editing experience. You can find and install these tools from the Visual Studio Gallery. The gallery is available as a web site (http://visualstudiogallery.msdn.microsoft.com/), or through the Visual Studio extension manager (go to the Tools menu, and select Extension Manager).In the following screen shot we’ve gone to the Visual Studio Extension Manager and searched the online gallery for “t4 editor”. The results include T4 specific editors from Tangible ( http://www.tangible-engineering.com/) and Clarius Consulting ( http://visualstudiogallery.msdn.microsoft.com/40a887aa-f3be-40ec-a85d-37044b239591/). These editors include various features, including support for Intellisense and syntax coloring.
Once you’ve finished editing, something might go wrong. If the processor fails to parse the template, you’ll receive error messages in the Error List window. For example, an extraneous <# delimiter will give you the following error messages.
You might also run into the scenario where the template processor parses the template but produces illegal C# code because of problem in the template. This scenario will also list errors in the Error List window. An example would be the template we demonstrated with a method defined inside a standard control block instead of inside a class feature block.
Finally, you might run into problems with the execution of the code inside the template. For example, you might see a null reference exception or incorrect output in the generated file because of a bug or miscalculation in your code. In these cases, it can be helpful to step through the template code with a debugger.
First, make sure you put the template into debug mode.
- <#@ template language="C#" debug="true" hostspecific="true"#>
- <#
- System.Diagnostics.Debugger.Launch();
- System.Diagnostics.Debugger.Break();
- #>
Now that we’ve covered the basics of T4 Template authoring, we can turn our attention to using T4 with the Entity Framework.
The Entity Framework and T4 Templates
At its core, the ADO.NET Entity Framework relies on an Entity Data Model. An EDM provides all the metadata the framework needs to translate LINQ queries into SQL commands and materialize objects from query results. This metadata includes a storage model (which describes a database schema), a conceptual model (which describes entities used in the application), and the mapping between the storage and conceptual models. One approach to creating an EDM is to right-click on a project, select Add New Item, and select the item template named ADO.NET Entity Data Model.Adding this item template to a project will create an XML file with an .edmx extension. The XML contents of the .edmx fully describe the conceptual and storage models, as well as the mapping between the two models. The Entity Designer also stores layout information in the .edmx file related to the graphical display of the models on the design surface in Visual Studio. When adding a new EDM item, you can choose to start with an empty model, or generate a model by selecting tables, views, and stored procedures in an existing database.
Regardless of how the EDM comes into existence, a developer can use the Entity Designer to construct a conceptual model of their domain. The conceptual model includes definitions for entities, entity properties, and the relationships between entities. For example, in building a conceptual model of an application to work with movie reviews, you might define Movie and Review entities and include a one to many relationship between the two entities.
The Entity Designer saves this conceptual model into the .edmx file using an XML derivative known as conceptual schema definition language (CSDL). While tools like the Entity Designer can readily consume CDSL, the developer who wants to work with the conceptual model inside an application needs CLR type definitions – not XML. In this example, we’d expect to work with a class named Movie and a class named Review.
Fortunately, the Entity Designer includes tools to generate code from an EDM. The default code generator is the EntityModelCodeGenerator, and it can transform CSDL into C# or Visual Basic code (creating the object layer, in Entity Framework parlance). You’ll see this default generator as the custom tool for an .edmx file by default. Whenever you save an .edmx file, the generator will execute and generate the object layer into a .cs or .vb file behind the .edmx file. This is similar to how T4 templates work, but we are not using templates yet.
If we open the generated Movies.Designer.cs file, we’ll find the following class definition for a Movie (some code omitted for brevity).
- [EdmEntityTypeAttribute(NamespaceName="MovieReviewsModel", Name="Movie")]
- [Serializable()]
- [DataContractAttribute(IsReference=true)]
- public partial class Movie : EntityObject
- {
- [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
- [DataMemberAttribute()]
- public int ID
- {
- get
- {
- return _ID;
- }
- set
- {
- if (_ID != value)
- {
- OnIDChanging(value);
- ReportPropertyChanging("ID");
- _ID = StructuralObject.SetValidValue(value);
- ReportPropertyChanged("ID");
- OnIDChanged();
- }
- }
- }
- ...
- }
Instead of trying to anticipate and fulfill every possible scenario and requirement, the Entity Framework team decided to use T4 templates as an extensibility mechanism. T4 templates allow developers to customize or completely replace the code generation strategy.
Existing T4 Templates for the Entity Framework
In addition to using the EntityModelCodeGenreator, the Entity Framework provides T4 templates for generating the object layer. There are two templates provided with Visual Studio 2010, and additional templates are available for download. To use one of these templates, right-click on the design surface of an .edmx file and select the “Add Code Generation Item” command.Selecting the command will launch a dialog box allowing you to select one of the installed code-generation items, or to search for new items online (a topic we’ll revisit later). The two templates installed with Visual Studio 2010 include the “ADO.NET EntityObject Generator”, and the “ADO.NET Self-Tracking Entity Generator”. We’ll look at the EntityObject Generator first.
The EntityObject Generator and Code Generation Strategies
The EntityObject Generator is a T4 template that generates the same default code as the EntityModelCodeGenerator. However, the EntityObject Generator comes in the form of an editable T4 template. The template will become a part of your project, and you can open the template to make modifications. The generated object layer will reflect these modifications. Let’s see how the process works.In the Add New Item dialog, you can enter a name for the EntityObject Generator. Like all T4 templates, the new item will have a .tt extension. If you look in the Solution Explorer window after you enter a name and click the Add button, you’ll find the .tt file added to the project. The custom tool for the .tt file will be the same TextTemplatingFileGenerator we saw earlier with our simple template. Just as with the simple template, the generator will transform this template into a code file. In the following screen shot, we’ve named the new item Movies.tt.
The file containing the template output is Movies.cs. As we mentioned earlier, this file will contain the same code we would see generated by the EntityModelCodeGenerator. Before we look at the template creating this code, let’s look at what is different about our .edmx file.
If you examine the properties of the conceptual model you’ll see the Code Generation Strategy value is set to “None”. You can examine the property by double-clicking the .edmx file to open the file in the designer, then right-clicking in the white space of the designer and selecting Properties. A value of “None” means the EntityModelCodeGenerator will no longer create an object layer in the Movies.Designer.cs file. The file still exists, but if you open the file you’ll only find a comment telling you that default code generation is disabled. If you want to return to using the default code generation strategy, change the value to “Default”.
It’s important to understand that even though Code Generation Strategy is set to “None” we are still generating code, and the EDM is still the authoritative source of the conceptual model. Looking inside the EntityObject generator template, you’ll find the following lines of code near the top of the template.
- UserSettings userSettings =
- new UserSettings
- {
- SourceCsdlPath = @"Movies.edmx",
- ReferenceCsdlPaths = new string[] {},
- FullyQualifySystemTypes = true,
- CreateContextAddToMethods = true,
- CamelCaseFields = false,
- };
The ADO.NET Self Tracking Entity Generator and Multiple Output Files
The second built-in template you can use to generate the object layer is the ADO.NET Self Tracking Entity Generator. The entities generated from this template are responsible for recording any state changes they experience. You can disconnect these entities from their object context, serialize and pass them into different tiers of an application, then reattach them at a later point to persist any recorded modifications.Unlike the EntityObject generator, the self-tracking entity generator adds two templates to a project. The first template generates the ObjectContext derived class you need to retrieve and save entities (this template ends with .Context.tt). The second template generates the entities themselves and an ObjectChangeTracker helper class. Unlike the templates we’ve seen so far, each of these templates generate multiple output files.
In the above screen shot, you can see a .cs file for each entity (Movie.cs and Review.cs). T4 templates do not directly support the generation of multiple files, so the template relies on a helper class named EntityFrameworkTemplateFileManager. The definition for this class is in EF.Utility.CS.ttinclude (EF.Utility.VB.include for VB templates), and all the EF templates we’ll look at include this file (it lives under the Visual Studio installation directory in Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes).
The EntityFrameworkTemplateFileManager is a class you can use in your own custom templates once you understand the simple API. The following template demonstrates the basics.
- <#@ template debug="true" hostspecific="true" language="C#" #>
- <#@ output extension=".txt" #>
- <#@ include file="EF.Utility.CS.ttinclude"#>
-
- <#
- var fileManager = EntityFrameworkTemplateFileManager.Create(this);
- fileManager.StartHeader();
- #>
- -- This line is a common header to use in every generated file.
- <#
- for(var i = 0; i < 3; i++)
- {
- fileManager.StartNewFile("file_" + i.ToString() + ".txt");
- #>
- Here is some content for file # <#= i.ToString() #>
- <#
- }
- fileManager.Process();
- #>
Finally, invoke the Process method when you are ready for the file manager to write all the separate files. With the above template, we’ll create 4 files (one output file for the template itself, and three files we’ve created with the StartNewFile method).
The contents of file_0.txt will look like the following.
- -- This line is a common header to use in every generated file.
- Here is some content for file # 0
Host Specific Templates
Note there is an extremely important setting in the template directive of the last sample.
- <#@ template debug="true" hostspecific="true" language="C#" #>
Additional Templates and the POCO Entity Generator and
Shortly after the release of EF 4.0 and Visual Studio 2010, developers from inside and outside of Microsoft began to provide additional T4 templates for use with the Entity Framework. You can install these additional templates from the Visual Studio Gallery.In the following screenshot, we’ve gone to the Visual Studio Extension Manager, selected the Online Gallery, and searched for the term “entity”. The top result in this search is the “ADO.NET C# POCO Entity Generator”. POCO stands for “plain old CLR object” and implies an object with no ties to data access APIs or 3rd party infrastructure. The EF team created this template to provide support for generating simple POCO class definitions from an EDM. Clicking Download will automatically download and install the template.
Once the extension is installed, you’ll be able to use the POCO template as easily as the built-in templates. Right-click on the Entity Designer to add a new code generation item, and you’ll be able to select the template from the list of available templates.
Like the self-tracking entity generator, the POCO generator will add multiple templates to a project, and use the EntityFrameworkTemplateFileManager to emit multiple files from a single template.
All three of the code generators have a similar internal structure and use the same helper classes. We’ll learn about these pieces in the next section when we customize a template.
Customizing Templates
When the available code generation templates don’t suit your needs, you have the choice of changing the existing templates or writing your own template from scratch. Obviously, if you are mostly happy with the output of a template, but you need some small changes, then modifying an existing template is the best approach. However, even if you want to start a template from scratch, there is reusable code provided by the EF templates to make the job easier.Entity Framework Metadata
The EDM contains a vast amount of metadata you’ll want to utilize in a custom template. The loading and parsing of the .edmx file containing this metadata is something the helper classes can manage for you. All three templates begin with essentially the following code.
- <#@ template language="C#" debug="false" hostspecific="true"#>
- <#@ include file="EF.Utility.CS.ttinclude"#>
- <#@ output extension=".cs"#>
- <#
-
- CodeGenerationTools code = new CodeGenerationTools(this);
- MetadataLoader loader = new MetadataLoader(this);
- CodeRegion region = new CodeRegion(this, 1);
- MetadataTools ef = new MetadataTools(this);
-
- string inputFile = @"..\Movies.edmx";
- EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
- string namespaceName = code.VsNamespaceSuggestion();
-
- EntityFrameworkTemplateFileManager fileManager =
- EntityFrameworkTemplateFileManager.Create(this);
- #>
The MetadataLoader is responsible for parsing an .edmx file into an EdmItemCollection. This collection class lives in the System.Data.Metadata.Edm namespace of the System.Data.Entity assembly (so it is part of the Entity Framework and .NET, and not just a helper class defined in a template). An EdmItemCollection will hold objects representing pieces of the conceptual model, like EntityType objects. The MetadataLoader can also load metadata from the StorageItemCollection and StorageMappingItemCollection. Not all templates will need this additional storage and mapping metadata, but you will find it used in the ADO.NET Self Tracking Entity Generator template.
The POCO generator uses the EdmItemCollection in the following code to begin creating entity class definitions.
- // Emit Entity Types
- foreach (EntityType entity in ItemCollection.GetItems<EntityType>()
- .OrderBy(e => e.Name))
- {
- fileManager.StartNewFile(entity.Name + ".cs");
-
- ...
A template can use an EntityType it retrieves from the EdmItemCollection to create properties for entity objects. The following foreach statement in the POCO template demonstrates this capability (it’s looping through the primitive properties explicitly declared on this type only).
- foreach (EdmProperty edmProperty in
- entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType &&
- p.DeclaringType == entity))
Code Generation Helpers
The included EF utilities also define CodeRegion and CodeGenerationTools to ease the pain of code formatting. For example, the CodeRegion class includes Begin and End methods to emit #region and #endregion into the generated C# code and omitting the region if the template does not generate code inside the region.. The CodeGenerationTools class includes overloaded version of an Escape method to transform string and EDM objects into strings you can use as legal C# identifiers.As an example, the POCO templates uses the CodeGenerationTools when writing out the class name of an entity.
- partial class <#=code.Escape(entity)#>
- <#=code.StringBefore(" : ", code.Escape(entity.BaseType))#>
Modifying the EntityObject Generator
Let’s customize the ADO.NET EntityObject Generator to include attributes from the System.ComponentModel.DataAnnotations namespace. Several validation frameworks recognize attributes in this namespace and use them to perform validation checks on an object. For example, the default model binder in ASP.NET MVC will look for attributes like [Required] and [StringLength] to ensure a given object is in a valid state.By default, the EF templates do not add any annotations to an object, yet there is information available in the EDM that will let us apply these attributes. Every EdmProperty object inherits a TypeUsage property, and a TypeUsage object includes a Facets collection. Facets contain additional information about a property, like it’s nullability and the maximum length (for string types).
The EntityObject Generator is a large template – currently over 1,200 lines of code, not counting the helper classes we reviewed earlier. However, if all you need to do is to make small changes to the default generated code, then modifying this template is easier than starting from scratch.
First, we can add our own helper methods in the template to retrieve the MaxLength and Nullable facets of a TypeUsage object. You’ll need to place these methods in the class feature block at the bottom of the template (in the current version of the EntityObject Generator template, the class feature blocks starts around line 723).
- private bool IsNullable(TypeUsage usage)
- {
- return (bool)usage.Facets.First(facet => facet.Name == "Nullable").Value;
- }
-
- private bool HasMaxLength(TypeUsage usage)
- {
- return usage.Facets.Any(facet => facet.Name == "MaxLength");
- }
-
- private int MaxLength(TypeUsage usage)
- {
- return (int)usage.Facets.First(facet => facet.Name == "MaxLength").Value;
- }
- using System;
- using System.Data.Objects;
- using System.Data.Objects.DataClasses;
- using System.Data.EntityClient;
- using System.ComponentModel;
- using System.Xml.Serialization;
- using System.Runtime.Serialization;
- using System.ComponentModel.DataAnnotations;
- foreach (EdmProperty property in
- complex.Properties.Where(p => p.DeclaringType == complex &&
- p.TypeUsage.EdmType is PrimitiveType))
- {
- VerifyGetterAndSetterAccessibilityCompatability(property);
- WritePrimitiveTypeProperty(property, code);
- }
- <#+ if (IsNullable(primitiveProperty.TypeUsage)) { #>
- [Required]
- <#+ } if(HasMaxLength(primitiveProperty.TypeUsage)) { #>
- [StringLength(<#=MaxLength(primitiveProperty.TypeUsage) #>)]
- <#+ } #>
- [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
- [DataMemberAttribute()]
- [Required]
- [StringLength(64)]
-
- public global::System.String Title
- {
- get
- {
- return _Title;
- }
- set
- {
- OnTitleChanging(value);
- ReportPropertyChanging("Title");
- _Title = StructuralObject.SetValidValue(value, false);
- ReportPropertyChanged("Title");
- OnTitleChanged();
- }
- }
Conclusion
In this paper we’ve looked at the capabilities of T4 Templates and demonstrated how the Entity Framework leverages templates for code generation. The EDM is still the centerpiece of the code generation strategy, and inside the templates we’ve pointed to some utility classes, like the MetadataLoader, that make working with the EDM easier. Even though the EDM metadata drives all the code generation by default, you can use the power of templates to pull in custom metadata from any data source and augment the generated code with custom members and attributes. Templates allow the best of both worlds – the code generation of strongly typed classes for an object layer and also the flexibility of customization and total control.Scott Allen
from msdn