Tuesday, April 23, 2024
HomeProductsADO.NET Data ProvidersExplanation of Microsoft POCO and Self-Tracking Templates

Explanation of Microsoft POCO and Self-Tracking Templates

If a developer needs to introduce certain changes or adjust the behavior of Microsoft’s POCO or Self-Tracking templates of the EntityFramework model or to write a custom template from scratch based on Microsoft’s standard template, he or she will have to learn how these templates work.

When studying Microsoft’s POCO and Self-Tracking generation templates, I have come across significant difficulties in understanding the exact way these templates work, since they are far from being trivial. In this blog article, I shall endeavor to describe their basic functions, explain some points that might be so easy to understand, as well as the structure of the templates. Hopefully, this information will prove useful and time- and effort-saving to all developers who need to work with Microsoft’s generation templates for EntityFramework models.

Microsoft’s standard templates are written in the T4 template language. You can find their description, procedures for adding, as well as a small customization sample here: http://msdn.microsoft.com/en-us/gg558520. This article provides basic information on T4 generation templates for EntityFramework that is required for their use, as well as details on how to work with metadata, generate several files and some other aspects.

Now let’s see how Microsoft’s POCO and Self-Tracking are designed and how they can be used in application development.

Template Files

Both templates consist of two files: <model name>.Context.tt and <model name>.tt. The <model name>.Context.tt template file generates the ObjectContext code of the EntityFramework model, while <model name>.tt generates classes for Entities and the model’s complex types, as well as additional classes that are required by the generated model to work properly. This POCO template file also generates the <model name>.cs file that contains the FixupCollection class required for notification in POCO models.

The Self-Tracking template also generates additional files containing classes and interfaces required by the model to work properly (for more information see http://msdn.microsoft.com/en-us/library/ff477604.aspx).

Classes, Functions and Structures Most Frequently Used in Templates

The WriteHeader function is used to generate the header of each template’s output file; it is responsible for generating the same-type code in the beginning of each file: comments that describe the file and declaration of the set of namespaces in use.

The CodeRegion class is used in templates to insert regions into generated code, as well as to manage indents in code. Its Begin(string) and End() methods are used to emit #region and #endregion into the generated C# code and to omit the region if the template does not generate code inside the region. The name of the region is sent to the Begin(string) function as a parameter, and the class itself calculates indents for the current region.

In templates, it is also possible to use CodeRegion.GetIndent(int), the static method of this class, to obtain a string of spaces equivalent to the number of indents desired.

The result returned by the CodeRegion.GetIndent(int) function is sent to the PushIndent(string) template function. This template function is responsible for setting indents for generating code, since it receives the value of the indent that will be appended as the prefix to each string of the template’s generated code. Thus, we can manage the formatting of generated code. The PopIndent() template method resets the current indent value and returns the previous value to be used for the purpose of formatting.

EntityFrameworkTemplateFileManager class is used in the template to manage output files. Its functionality is well-described in the MSDN article (see the link in the beginning of this article), so I will not dwell on this topic any further.

CodeGenerationTools Class

This class is most often used in templates. In the beginning of the template, you can see the “code” local variable is created; this variable is then frequently used within the template. Escape, its most frequently used function, has lots of overloads and is used to transform a string representation of the object in a way that is safe for the compiler.

For example, your model has an entity called “class”; this name is the name of the class for that entity and the following code is generated for it:

public class <entity name>

However, it is impossible to use the “class” identifier for the C# compiler, since the compiler will not process a code like:

public class class …

To use the reserved “class” word as the name of a class, we have to transform it into the compiler-legal form:

public class @class …

The Escape function checks if it is necessary to transform the identifier into the safe form and, if that is so, performs the transformation and returns an always legal identifier. The EscapeNamespace function uses the same workflow and always returns an identifier that can legally be used as the name of a namespace.

The second most frequently used function of this class, FieldName, returns the name of the internal field for a class member, with this name being formed as the class member name with the prefix “_” .

The AbstractOption function checks whether the class is defined in the metadata of the model as abstract and, if that is so, returns a compiler-legal identifier of the abstract class.

The CreateLiteral(object value) function transforms an object into the code string for its initialization by the compiler. For example, if a value of the System.Guid type is sent to the function, then we get the following string: “new Guid(“<string representation of the sent object>”)”. This function is used to generate the code that initializes the default values of the properties of entities, if the default values are defined in the metadata.

I will not describe other functions of the class, since they are seldom used and intuitively understandable.

Accessibility Class

This class is also used in templates often enough and is a set of static functions, which returns a representation of a compiler-legal object access identifier.

Other Structures

When working with navigational structures, one can often come across the structure ((AssociationType)<navigation property name>.RelationshipType).IsForeignKey. This structure returns True if the association has a referential constraint. The <navigation property name>.ToEndMember.GetEntityType() returns the entity referenced by the navigation property. The method GetDependentProperties() gets the collection of properties that are on the dependent end of a referential constraint for the specified navigation property. The GetPrincipalProperties method of the navigation property gets the collection of properties that are on the principal end of a referential constraint for the specified navigation property.

Peculiarities of <model name>.Context.tt Template

The <model name>.Context.tt contains no difficulties or aspects, which have not been described above, and generates rather trivial code of the context class that includes ObjectSet properties and function imports.

For Self-Tracking generation, this template also contains code for the generation of the additional file <model name>.Context.Extensions.cs:

fileManager.StartNewFile
    (Path.GetFileNameWithoutExtension(Host.TemplateFile) + ".Extensions.cs");
    BeginNamespace(namespaceName, code);
    WriteApplyChanges(code);  // generates the main content of the file
    EndNamespace(namespaceName);

Peculiarities of <model name>.tt Template

The <model name>.tt is significantly more complex. It is worth saying from the outset that one should not be dismayed by a large volume of code in the Self-Tracking template – this code is not as difficult as it might seem at first glance.

The code for the generation of <model name>.cs is placed in the very beginning of the template.
In the template for POCO, this looks like:

WriteHeader(fileManager);
BeginNamespace(namespaceName, code);
WriteCustomObservableCollection();// generates the main content of the file
EndNamespace(namespaceName);

The generated file contains the FixupCollection class that is required for notification in POCO models.

In the template for Self-Tracking, this looks like:

WriteHeader(fileManager);
BeginNamespace(namespaceName, code);
WriteObjectChangeTracker();
WriteIObjectWithChangeTracker();
WriteCustomObservableCollection();
WriteINotifyComplexPropertyChanging();
WriteEqualityComparer();
EndNamespace(namespaceName);

The generated file contains all classes and interfaces that are required for the model to work properly. For more information on the subject, see http://msdn.microsoft.com/en-us/library/ff477604.aspx.

Entity Generation

The template is iterated on the model’s entities and that results in generating classes for each entity. The generation of class definition takes possible inheritance into account. Class generation is divided into the following regions: Primitive Properties Generation, Complex Properties generation (whose type is complex), Navigation Properties generation, and Association Fixup generation. For the Self-Tracking template, the ChangeTracking region is also generated.

Primitive Properties Region

The Primitive Properties region contains generated entity properties of the simple (not complex) type. When the default value is set for the property, this value is used to generate initialization code that is performed by the CreateLiteral property of the CodeGenerationTools class. If the property is a foreign key property, that is, if there is a navigation property that references the Master class for the entity and the generated primitive property participates in the Referentional Constraint of this navigation property association, then additional code is generated by the setter method for the property to keep association fixup.

In the Self-Tracking template, the IsOriginalValueMember(EdmProperty edmProperty) method of the OriginalValueMembers class returns True if the СoncurrencyMode property of a generated property has the Fixed value or if the generated property is a foreign key property.

Complex Properties Region

The Complex Properties region contains generated entity properties of the complex type.
The POCO template for the class field generates the initialization code with a new value of this complex type.
The Self-Tracking template does not perform this operation, since, in the code it generates, initialization of a complex type field is performed on the setter method of the corresponding property.

Navigation Properties Region

The Navigation Properties region contains generated navigation properties of the entity. Depending on the Multiplicity of the navigation property, a property containing either an instance of the master class for the entity or a collection of details classes is generated.

Peculiarities of MetadataTools class methods for Self-Tracking templates

When generating a navigation property with Multiplicity = Many, the IsCascadeDeletePrincipal(NavigationProperty navProperty) method of the MetadataTools class returns True, if the navigation property’s OnDelete Action has the Cascade value, or if the collection of properties that are on the principal end of a referential constraint for the specified navigation property contains at least one entity key property.

When generating a navigation property with Multiplicity = Many, the IsPrincipalEndOfIdentifyingRelationship(AssociationEndMember associationEnd) method of the MetadataTools class returns True, if the collection of properties that are on the dependent end of a referential constraint for the specified navigation property contains at least one entity key property.

ChangeTracking Region in Self-Tracking Template

The ChangeTracking region that is available only in the Self-Tracking template contains the generated ChangeTracker property and methods that with relationships synchronization logic.

In this region, it is worth paying attention to the IsSaveReference(MetadataTools tools, NavigationProperty navProperty) method of the template itself. This method returns True, if this is a foreign key association that is added without adding foreign key properties, that is, a referential constraint is not used in its mapping.

Association Fixup Region

The Association Fixup region contains generated methods that are used to update associations and navigation properties when changes are introduced. Besides the functions described above, all the other aspects of this region are few and intuitively understandable.

Complex Type Generation

After generating entities, the iteration proceeds to the model’s complex types, for which classes are generated. This includes generating a class definition, as well as Primitive and Complex properties. For the Self-Tracking template, ChangeTracking is additionally generated.

Hopefully, this article will help you master Microsoft’s standard POCO and Self-Tracking templates for generating EntityFramework models.

RELATED ARTICLES

1 COMMENT

  1. Explanation of Microsoft POCO and Self-Tracking Templates…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

Comments are closed.

Whitepaper

Social

Topics

Products