This project has moved. For the latest updates, please go here.

TDS Example – Editing a Text Template File

 

Purpose


The examples in the tutorial in the published TDS.cs are designed to illustrate the features and capabilities of the TDS code to help with developing C# function members of types. Those examples are intended to be short and easy to understand, and most of the products illustrated there do nothing useful.

In contrast, the present page contains an example of a project to do something a bit different, and slightly more complex – using a TDS test method to help develop a T4 text template file, which generates an XML schema file, which helps to generate an XML database dictionary table (some assembly required), which describes an XML database that describes a book collection. Several of these example files are downloadable from this page (but you’ll have to supply your own book collection, sorry about that). The products in this example might even have some practical value for you, if you wish to design an XML database, or if you might wish to use this example to construct a database dictionary (descriptions of tables and fields) in a database of your design. (That database, which in this example describes the book collection, is not illustrated here. Use your imagination.) Please feel free to have fun with this example, or even to put it to work if you wish.

Note: This example does illustrate how to write, edit, and test the class feature control block of a .T4 file as code, taking advantage of VS's editing facilities, such as IntelliSense, but the discussion here integrates that with the TDS platform. If your primary interest is simply editing or generating .T4 files, you might instead consider using the free-of-charge tangible T4 Editor to do that. I haven't used it and don't know if it's compatible with VS Express, but I have seen it recommended.

The explanations shown here assume that you are familiar with TDS. Even if you have not completed the TDS tutorial, you are of course welcome to browse this material, but it will probably make more sense if you have already built those examples. (I estimate that building the examples the first time should take a couple of hours.)

Although here I spare you the details of my numerous redesigns of the T4 file, you may be able to infer some of them from these mostly finished files. I hope that you can similarly infer a process for designing, formatting, and debugging, with the help of TDS, your own T4 files. (I do provide a few explicit hints.) For example, you might try making changes to the files presented here to observe the effects of the changes. You may wish to imitate the development process with your own version of TDS and your own TDS test methods, in constructing a T4 to produce the XSD for a different kind of XML database (for downloaded videos, perhaps?), or for some other file format (for example, VB or C# code, HTML for a Web page, or another T4).

In this example the T4 file contains a mixture of C# and XML code to generate an XSD schema file. The included files represent the state of the project at an advanced stage – in these files, debugging of the T4 file is essentially complete, so at this point the TDS is being used mostly to verify that refactorings have no adverse effects. For example, if I change the wording of a class of comments, I can then run the TDS test method to show that the results are consistent with what I expected (or perhaps that there’s a bug that I need to correct).

You might notice that, whereas the original TDS.cs is in the public domain, these example files are copyrighted. This is intentional; I wanted the basic TDS.cs file to be as easy as possible to use (i.e., with no copyright restrictions). You may still use and modify the present examples fairly freely, but I would appreciate your preserving my copyright notices on these.

Included files

Original XSD schema file

(File DbDictionary_Pattern.xsd)

Written by hand as an XML file, this was my original model for the T4 file. During development, I replaced parts of the XSD code with T4 control blocks, many of which contained C# code that I could test using TDS before copying it into the T4 file.

Later, I used this file as the basis for comparison with XSD generated by T4 to either (1) identify errors in the T4 file or (2) to detect inconsistencies between the original and generated XSD files, such as elements that are intended to be similar but that actually have non-matching structures. I would then make suitable corrections to the original XSD, the generating T4, or both, to make them match.

Example TDS file

(File TDS_14E29b.cs)

This example TDS file includes two test methods, one to exercise and test the methods used in the class feature control block of the T4 file, and one to demonstrate that the C# types (such as DatabaseDict{}) generated from the XSD schema file generated by the T4 file are behaving as expected.

Unlike the example test methods in the TDS tutorial, which mostly just call the function members being developed, in this version I did substantial work within the TDS test methods themselves. This made sense especially for the DbDictionary_PatternT4Test() test method, whose testable code is located in its “#region Class feature control block contents” region. In the case of the DbDictSampleTest() test method, which illustrates some simple uses of the DatabaseDict{} class, this is less justifiable. If that code were to do more work than (as it does) merely to display some field contents, it should probably be moved to a namespace independent of the TDS test methods and called by a separate TDS test method.

This version of the TDS.cs file is probably not a good basis for developing your own TDS tests; it’s been substantially mangled to suit my purposes, as I expect you would do with it for your own projects. For example, the tutorial and examples are stripped out of this one, and the TestableConsoleMethodTest() test method is repurposed to exercise the methods to be copied to the T4 file. Since I frequently changed the state of the “//#define RunOnlySelectedTestData” line during development, I placed a copy of it at the beginning of my TDS_14E29b.cs to make that line easy to find whenever I needed to change it.

Example T4 file

(File DbDictionary_Pattern.t4)

The DbDictionary_Pattern.t4 file generates the XSD file that governs the structure of the XML database file.

Its class feature control block is copied from the TDS.cs file (in this case, from TDS_14E29b.cs), and it is replaced (in toto) every time that anything in the TDS.cs version of the same code is changed. The class feature control block code in the TDS file is what is exercised by the test method; this code is located in the “#region Class feature control block contents” region near the end of the TDS.cs file, making it easy to copy to the T4 file. I did this manually, many times, and it may seem more tedious than it is – collapsing the #region before copying allowed me to replace it in the T4 file in 10 or 15 seconds. For this project, that short time made it seem unnecessary to develop an automatic technique for updating the T4.

The main reason that I maintain two copies of the class feature control block is that, in VS 2013, the editing features available for T4 files are about as powerful as those of the Notepad text editor – no syntax coloring, no automatic indentation, no IntelliSense pop-ups, no outlining, no XML comment descriptions in VS’s Object Browser, no IntelliSense support for editing the XML comments, etc. I figured that being able to use those features in VS’s C# editor made the work of copying the code into the T4 well worth the small effort required.

Most of the interesting specifications for this XSD file are located in the code (text blocks and control blocks) in the first 120 or so lines of the T4 file.

One benefit of using a T4 file like this is that it is more concise (not counting the class feature control block) than the generated XSD, and some types of edits to it can have coordinated effects at multiple locations within the generated file. (An example would be the text in one of the “Generic” comments in the CommentsText string, variations of which may appear in several locations in the XSD.)

In this T4 file, three regions beginning with “#if false” disable the generation of all of the elements except for Tables and Fields, simplifying the XSD. If these are changed to “#if !false”, the generated XSD becomes 730 lines long instead of the current (roughly) 216 lines. (This is an illustration of a possible use of conditional code, though it may not be of great practical value – if you really needed to reconfigure your generated file frequently, you would probably prefer to do that via a method in the class feature control block.) The structure of the longer version, obtainable by de-commenting parts of the T4 file, is illustrated here:

DiagramOfFullDb


as a Visual Studio database diagram. The structure of the shorter, simplified version (corresponding to this XSD) is illustrated here:

DiagramOfBasicDb



Note: In an earlier version of this T4, I wrote the methods as a set of Funcs and Actions, placing them in a standard control block near the beginning of the T4 file. This worked, but I thought it made the file difficult to read, as the first text block, containing much of the XML code, was pushed down toward the end of the file, past hundreds of lines of C# code. In the present version, most of the C# code follows the text block. The file would probably be even easier to read if the C# methods in the class feature control block were located in a separate file, to be imported via an <#@include#> directive, but for this project I did not want to introduce an additional file. For reasons of style, you might prefer using <#@include#> directives. Be my guest.

Generated XSD schema file (short version)

(File DbDictionary_Pattern.xsd)

While the T4 was under development, it generated versions of this file that could be compared (e.g., via WinDiff or FC) with the original XSD. Changes to both the T4 and the manual XSD caused them to converge to a single, apparently bug-free, version.

This finished XSD file governs the structure of the XML database that is exercised in TDS test method DbDictSampleTest().

(Since, by the time the T4 is stable, this generated XSD is identical to the original one, no duplicate version of the file is posted here. However, throughout development, I did maintain two separate versions of this XSD.)

Corresponding XML database

(File DbDictSample.xml)

An example XML database file based on the XSD, and accessible via what I named the DatabaseDict{} class, is also included. (“DatabaseDict” is the name of the top-level element in the schema, and the class is generated based on this name.)

This file was written largely by hand, assisted substantially by Visual Studio’s IntelliSense services. For an example of this assistance, observe the result of adding a new, initially empty XML file to an open VS project, opening it for editing, and linking it (via VS menu “XML, Schemas…”) to the XSD file DbDictionary_Pattern.xsd:

Xml01SchemaSelection

 

When I type an opening “<” bracket, an IntelliSense pop-up menu that magically appears gives me the option of adding a “DatabaseDict” element, and that also displays a description of what that element does:

Xml02aIntelliSense

 

The XML editor in VS offers various editing aids, such as formatting, syntax coloring, and outlining, and if I omit any required elements the editor displays error messages.  For example, when I have added the <TablesAndAssociations> opening and closing tags, I can see the IntelliSense comments for them when I hover the mouse pointer over the </TablesAndAssociations> tag.  In this example, we also see the unmatched <DatabaseDict> tag flagged as an error:

Xml03IntelliSenseHover

 

The result is that, as I set up a new XML database depending on this schema, most of the work that I must do is to enter the correct names, types, and descriptions of the elements; the XML boilerplate is automatically supplied.

This file contains descriptions of three tables in a database describing some books, and of the fields in those tables, and it is exercised by TDS test method DbDictSampleTest().

Result of running the TDS test

If you set up a VS solution containing a VS project “TDS” (the startup project) containing this TDS14E29b.cs file and a VS project “DisplayDbDictionary” that contains DbDictionaryPattern.t4 and DbDictSample.xml and set suitable references, you should be able to run the solution and produce a report similar to the following one, indicating the pretty much nothing remains to be done in this project:

 

***** Test{} class's static constructor has been called.

***** InitializeClasses() has begun running.

***** The following conditional compilation directive is

      included in TDS source-code file TDS_14E29b.cs:

          #define TDS_platform

***** TDS.Test.AllTestsAreToBeRunTest()

***** InitializeTestMethod() was called at 2014-05-30T01:58:56.5115732-05:00 .

***** CleanupTestMethod() is complete.

***** (End of test)

***** TDS.Test.DbDictionary_PatternT4Test()

***** InitializeTestMethod() was called at 2014-05-30T01:58:56.5335744-05:00 .

***** CleanupTestMethod() is complete.

***** (End of test)

***** TDS.Test.DbDictSampleTest()

***** InitializeTestMethod() was called at 2014-05-30T01:58:56.5895776-05:00 .

***** CleanupTestMethod() is complete.

***** (End of test)

***** The final test was completed at 2014-05-30T01:58:56.6445808-05:00 .

***** CleanupTestSession() is complete.

________________

*****  This was a test run.  The following results were generated. *****

________________

Passed tests

  The following 3 test methods returned a status of Passed:

  - AllTestsAreToBeRunTest()

  - DbDictionary_PatternT4Test()

  - DbDictSampleTest()

________________

No called test method returned a status of Failed.

________________

No called test method returned a status of Inconclusive.

________________

All TDS methods that have [TestMethod] attributes

    are in the TestMethodsToBeRun list.

All TDS methods that are in the TestMethodsToBeRun list

    have [TestMethod] attributes.

________________

Passed: 3  Failed: 0  Inconclusive: 0

________________

  All listed TDS test methods passed.

***** (End of test summary)

Press the <Enter> key to finish . . .

Something that is not shown here directly is that, if you wish to change any of the C# code in the T4 file, I suggest that you do so within TDS_14E29b.cs, test it to check that the change appears to work, and then copy the changed code into the T4 file, to keep the two copies consistent.

Running the example

Set up a new Solution in Visual Studio

  • Create an empty folder in Windows Explorer.
  • Copy into the folder the files TDS_14E29b.cs, DbDictionary_Pattern.t4, and DbDictSample.xml .
  • Open Microsoft Visual Studio Express 2013 for Windows Desktop. (If you don't have a copy, it's free of charge from Microsoft.)
  • Close the “Open Project” window, since the project does not yet exist.
  • Use menu “File, New Project…” to open a new Project.
  • Choose “Templates, Visual C#, Windows, Console Application”.
  • Browse to the folder to which you copied the three files.
  • Change the project name from “ConsoleApplication1” to “TDS”
  • Uncheck the “Create directory for solution” box. (A directory will still be created for the project.)
  • Click “OK”.
  • In the TDS Project, delete “Program.cs”.
  • In the Solution Explorer window, rename the Solution from “TDS” to “DbDictionary”.

Add a second Project

  • To the Solution, add a new C# Windows Class Library Project, and change its name from “ClassLibrary1” to “DisplayDbDictionary”. Click “OK”.
  • Delete file “Class1.cs” from this Project. (Don’t worry about starving the Project of code; it’ll get plenty from the XSD.)

Populate the TDS Project

  • To project TDS, add existing item "TDS_14E20b.cs”, from the parent folder.
  • Right-click project DisplayDbDictionary; choose “Add, Existing Item…” and set the file filter at the right of the File name box to “All Files”.
  • Add existing items “DbDictionary_Pattern.t4” and “DbDictSample.xml” from the parent folder.
  • In the Properties window for DbDictionary_Pattern.t4, set the Custom Tool property to "TextTemplatingFileGenerator"; press Enter.
  • In the Properties window for the newly generated DbDictionary_Pattern.xsd, (shown below DbDictionary_Pattern.t4 in the Solution Explorer window), set the Custom Tool property to “MSDataSetGenerator” and set the Copy to Output Directory property to “Copy always”.
  • In the Properties window for DbDictSample.xml, set the Copy to Output Directory property to “Copy always”.
  • To project TDS, add a reference (in the “Solution, Projects” tab of the Reference Manager window) to project “DisplayDbDictionary”.
  • To project TDS, add references (in the “Assemblies, Framework” tab) to “Sytem.Data.Entity”.

Smoke test

  • Use Debug, Start Debugging (or press F5) to run TDS. We hope to see, at the end of the TDS report, the message “ All listed TDS test methods passed.”. (“Smoke test” is a reference to hardware prototypes, where we first apply power to a new circuit and hope that we don’t see or smell any smoke.) If you get any error message, correct the problem before continuing.

Exercise the system by
     intentionally creating errors

Verify that this XML is being validated

  • To verify that the format of this XML file is what is actually being tested, open DbDictSample.xml for editing. Use VS menu “XML, Schemas” and select DbDictionary_Pattern.xsd as a schema that the editor should use.
  • Edit line 13, which should read “<Table tableName="Book">”, to change it to read “<Table tablename="Boom">”. This should cause two errors: an XSD validation error (no upper-case “N” in “tableName”), and a mismatch with the expected file contents (misspelled “Book”).
  • Use “Build, Clean Solution” to ensure that the files are updated, then F5 to run the tests. If a window with the message “An exception of the typeTDS.Assert…” appears, uncheck the box to break for that kind of exception and click “Continue”.
  • The report should contain the following message:

 

An unexpected exception was thrown:

  The 'tablename' attribute is not declared.

Verify that the C# code is returning expected values

  • Correcting “tableName”, using “Build, Clean Solution”, and rerunning with the second misspelling still present should give us the following message, followed by the rather verbose expected and actual messages. (For long messages, I usually use WinDiff or FC to compare them if the differences are not obvious.):

 

Assert.AreEqual failed. Expected:


Correct “Book” , use “Build, Clean Solution”, and rerun; the report should once again end with “All listed TDS test methods passed.

Verify that the run-time schema is the one generated by the T4 file

  • To verify that the T4 file is what is doing the checking, change it and rerun the program. For example, change line 47 from “T90TopLevelElement(14, "Table" ” to “T90TopLevelElement(14, "Tablet" ”. The report should contain this:

 

The element 'TablesAndAssociations' has invalid child element 'Table'. List of possible elements expected: 'Tablet'.

Rename elements in the XML file to match the new T4 file

  • If we edit DbDictSample.xml to change all of the “<Table>” elements to “<Tablet>”, all “tableName” attributes to “tabletName”, and all “<TableDescription>” elements to “<TabletDescription>”, we no longer get messages that the XML is ill-formed, since it now matches the changed XSD. However, we do get messages like this from the C# side of the process:

 

Assert.IsTrue failed.
DbDictSampleTest(), test case 01 Sample test.
  An unexpected exception was thrown:
    Value cannot be null.
Parameter name: source

Revise the C# code to match the new name

  • This message could be more informative, but setting a breakpoint and tracing quickly reveals that the problem is in the DbDictSampleTest() code, in lines

      from t in dbDict.Tables["Table"].AsEnumerable()

         and

          from t in dbDict.Tables["TableDescription"]

This shows that using the strongly typed DatabaseDict{} class keeps us from accidentally accessing some misspelled code. Changing these two C# lines completes the renaming of “Table” (as far as these tests are concerned) and again allows the program to run without reporting any errors.

Summary

Using these files, we have constructed a Visual Studio Solution in which we begin with a T4 text template file and the TDS code that helps us maintain it. The program uses the T4 file to generate and exercise C# code to access an example XML database in a type-safe manner and to display some of the XML file’s contents. We have also observed how some (intentional, in this instance) manglings of the C# or T4 or XML code affect the TDS test report.

Last edited Sep 2, 2014 at 4:01 AM by vjohns, version 4