Fun with T4 templates and Dynamic Data


T4 templates have been a pretty popular topic lately.  If you have no idea what they are, don’t feel bad, I didn’t either only a couple weeks ago!  In a nutshell, it’s a simple template processor that’s built into VS and allows for all kind of cool code generation scenario.  For a bunch of information about them, check out the following two blogs:



The basic idea is that you can drop a hello.tt file into a project, and it will generate a hello.cs (or hello.anything) file via its template.  The template syntax of .tt files is very similar to ASP.NET’s <% … %> blocks, except that they use <# … #> instead.


For a cool example of what you can do with T4, check out this post from Danny Simmons, which  shares out a .tt which generates all the Entity Framework classes automatically from an edmx file.  What’s really nice is that you can easily customize the template to generate exactly the code that you want, instead of being locked into what the designer normally generates.


So how does that have anything to do with Dynamic Data?


One weakness we currently have with Dynamic Data is that we don’t have a great way to get started with a custom page.  Suppose you start out with a web app in full scaffold mode, and find the need to customize the Details page for Products.  The ‘standard’ procedure to do this is:



  • Create a Products folder under ~/DynamicData/CustomPages

  • Copy ~/DynamicData/PageTemplates/Details.aspx into  it

  • Modify the copied file to do the customization you need

While this generally works, it has one big weakness: you need to start your customization from a ‘generic’ file which knows nothing about your specific table.  e.g. you start out with a <asp:DetailsView> control that uses AutoGenerateRows to generate all the fields.  So if you want to do any kind of real UI customization, you’ll likely have to do a bunch of repetitive steps, e.g.



  • Get rid of the DetailsView and replace it with a FormView (or ListView) so you can get full control over the layout

  • In the FormView’s item template, you’ll then need to add one <asp:DynamicControl> for each field that you want to display, along with formatting UI between them (e.g. <tr>/<td>).

A T4 template to the rescue


In a few hours, I was able to put together a quick T4 template that replaces most of those repetitive steps.  The steps using this template become simply:



  • Create a Products folder under ~/DynamicData/CustomPages (as above)

  • Drop Details.tt into it, and instantly it generates the aspx file, all expanded out with a FormView and all the DynamicControls! This .tt file is attached at the end of this post.

While this is cool, there is one thing that is a bit strange about it: you’re left with a .tt file you don’t want in your project:


image


And since details.tt file generates details.aspx file, you can’t really change the aspx until you delete the .tt file.  Unfortunately, VS makes this more difficult than you would think because the two files are linked to each other.  The best steps I found to delete it are:



  • Right click the .tt file and choose Exclude From Project

  • Right click the Products folder in open it in Windows Explorer

  • Delete the .tt file in the explorer

  • Back in VS, click Show All Files in the solution explorer.  You should see the aspx

  • Now right click it and Include it in the project

I know, that’s painful!!  Ideally, what we really want to do is use the .tt do to a one time transform, but never actually have it be part of the project.  At this point, I’m not yet sure how to do this, but I’m hopeful that there is a way.  Suggestions are welcome! :)



How does the template work?


In order to perform its task, the template needs to figure out what all the entity type’s fields are for the custom page.  Doing this is a bit non-trivial, and what I do here is not as clean as it could be, as this is just a quick prototype.  Here is a brief description of what it does:



  • Based on the folder name, it knows what the Entity Set name is (e.g. Products)

  • It then locates the app’s assembly in bin (caveat: this only works in Web Apps, not Web Sites!)

  • It copies it to a temp location and loads it from there, to avoid locking the bin assembly

  • In the assembly, it finds the DataContext/ObjectContext (it handles both Linq To Sql and Entity Framework)

  • From the context and the entity set name, and can know what the Entity Type is

  • Once it has that, it knows the fields and can generate the page!

If you look at the t4 files, you’ll see all this logic at the end.  That part is certainly quick and dirty, and could use some rewriting.  But what comes before it is a pretty clean template that’s easy to tweak to your needs.  e.g.

                    <table class=”detailstable”>
<# foreach (var prop in data.Properties) { #>
<tr>
<th>
<#= prop.Name #>
</th>
<td>
<asp:DynamicControl DataField=”
<#= prop.Name #>” runat=”server” />
</td>
</tr>
<# } #>
</table>

As you can see, it looks very much like inline code in an aspx file, except that instead of executing at runtime on the web server, it executes in Visual Studio to generate a file that becomes part of the project.


What about all the other views?


So I’m only talking about Details.tt/Details.aspx.  What about the other Dynamic Data views: List, Edit, Insert?  Well, those would all work in pretty much the same way, and I simply haven’t had a chance to get to them.  For now, I just wanted to get this first T4 file out as a proof of concept that shows the kind of things that can be done.  Contributions are welcome! :)


Conclusion


Clearly, a lot of what I describe here is pretty raw, and it’s only a very early prototype.  But I think it should be enough to convey the potential power of design time file generation via T4 templates.  Later, I’d like to get better integration with VS.  e.g instead of dropping the .tt file in the project and later having to delete it, it’d be nice to get a custom action by right clicking on the CustomPages folder, that would let you generate the aspx file.  It would still use T4 under the cover, but you wouldn’t have to see it unless you want to.


Hopefully, I’ll  be writing more about this in the future if there is interest.

Details.zip

Comments (15)

  1. Lee says:

    I don’t like t4 templates for the reasons you outline.  I prefer to use a code generator to generate the code for me and then i can simply drop it into my solution rather than having to be coupled with my files and the t4 template.  I built my own generator http://www.flixon.com/site-generator/ which was quickly knocked together but saves me hours of programming.  It uses a template engine and is fully customizable.  I think it would be great if t4 templates could be changed to use this approach.

  2. giorgio says:

    idem as asp classic: spaghetti code by net framework!!

  3. David,

    There’s plenty of interest on my part. For example, see my story that included Damien Guard’s T4 template for LINQ to SQL (http://oakleafblog.blogspot.com/2008/11/linq-to-sql-featured-in-visual-studio.html as an example.)

    –rj

  4. davidebb says:

    Lee, I think we need to separate two things:

    1. T4 as a templating language

    2. The coupling behavior of T4 files with generated files in the project.

    We both agree that in this scenario, the coupling is not desirable since we really don’t want to keep the .tt file around.

    However, I think using the T4 templating language makes a lot of sense (simple, user customizable, …).  It’s a matter of invoking it in a different way that doesn’t add it to the project, which as I understand can be done.

  5. davidebb says:

    Giorgio,

    ‘Spaghetti code’ occurs when you mix Controller logic with View logic.  While it is possible to do that in T4 and end up with bad code, you can easily achieve a very clean View (in the MVC sense) by separating the logic that comes up with the data from the logic that renders it (as I did in this .tt file).

  6. D. Lambert says:

    If you’re interested in another example of T4 template use, check out Rob Conery’s SubSonic.  It’s a pretty cool scaffolding tool for .Net – sort of inspired by ROR’s ActiveRecord.  I’m not positive that earlier versions used T4, but I’ve been playing with 3.0 Preview 2, and it’s using T4 to generate the scaffolded code.  IMO, it works pretty well – only a couple issues I’ve seen per my recent blog entry.

  7. davidebb says:

    Thanks Lambert, I’ll take a closer look at what Rob has been up to with T4.  I did hear from other that he did some nice things.  As I understand he manages to process the T4 without it ever getting added to the project.

  8. GB says:

    Has anybody got an example of using the T4 template ,I mean calling them from a windows form application passing parameters etc…

    that would be very useful

  9. Track back from DotNetLinks.net

  10. D. Lambert says:

    No, you’ve still got templates added to your project w/ SubSonic, but there’s less code to generate than there’d be without the SubSonic lib to support the code.  It’s not perfect, but it feels less cumbersome than this example because the generated code can remain generated – it’s ok to keep the tt files and re-gen when needed.  You can sort of forget how those classes are created and just use them.

  11. Andy says:

    I’m very keen for the .NET teams to produce T4 templates on Codeplex or MDSN Code Gallery for each of the ASP.NET and Data Access (LINQ) technologies that use some form of code generation.

  12. dka says:

    I was recently brought in to do some work on a (semi) popular web portal the serves the ##### community.  In the second week, after doing a quick ER model to support the new system, I set out to build CRUD SPs to allow front-end developers to consume and maintain data.  I choose to use T4 to generate these SPs which were modeled after similar SP already in play by this client.  Well, at the end of the week I was let go for low productivity reasons.  Upon discussion with the lead engineer he let me know that techniques like T4 are the definition of low productivity!  Same with Linq and the notion of building a wrapper class to make the use of the CRUD that much easier.  Anyone ever hear of this argument?  Changing my mind and feeling regarding this topic will be impossible but I’d at least like to hear if there are others that feel like my former boss does.  At the very least I can learn how to avoid this lose-lose situation.

  13. brett says:

    You might want to check out Appventure Foundations (http://www.appventure.com).  It’s a visual modeler that uses the t4 engine as its backend to generate code.

  14. Please post corrections/new submissions to the Dynamic Data Forum . Put FAQ Submission/Correction in

  15. Jenny says:

    Your article looks like it will save a lot of work.  I did try to use tt but got the following error (running VS2010 with .net 4.0):

    Error 3 Running transformation: System.InvalidOperationException: Sequence contains no elements

      at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)

      at Microsoft.VisualStudio.TextTemplatingA54A778C225264F6FC81681B5811ED87.GeneratedTextTransformation.EntityMetaData.FindContextType(Assembly binAssembly) in c:ProjectsSHS.CHA.WebQIModelInterventionsDynamicDataCustomPagesIncentiveValueDetails.tt:line 199

      at Microsoft.VisualStudio.TextTemplatingA54A778C225264F6FC81681B5811ED87.GeneratedTextTransformation.EntityMetaData.GetEntityType() in c:ProjectsSHS.CHA.WebQIModelInterventionsDynamicDataCustomPagesIncentiveValueDetails.tt:line 185

      at Microsoft.VisualStudio.TextTemplatingA54A778C225264F6FC81681B5811ED87.GeneratedTextTransformation.EntityMetaData.Process() in c:ProjectsSHS.CHA.WebQIModelInterventionsDynamicDataCustomPagesIncentiveValueDetails.tt:line 126

      at Microsoft.VisualStudio.TextTemplatingA54A778C225264F6FC81681B5811ED87.GeneratedTextTransformation.EntityMetaData.GetEntityMetaData(String templateFile) in c:ProjectsSHS.CHA.WebQIModelInterventionsDynamicDataCustomPagesIncentiveValueDetails.tt:line 117

      at Microsoft.VisualStudio.TextTemplatingA54A778C225264F6FC81681B5811ED87.GeneratedTextTransformation.TransformText() in c:ProjectsSHS.CHA.WebQIModelInterventionsDynamicDataCustomPagesIncentiveValueDetails.tt:line 87

      at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result) 1 1