Introduction
A few days ago I made a post on how to make a factory with T4 templates. That version only made one factory for one namespace. So it was pretty limiting. But as you could see, in that example, we had 2 namespaces, namely Test and Test2.
We need a factory for both those namespaces. We can either copy/paste or change our template to produce more files. After all, these files are nearly the same.
The code
The code for this is pretty simple, once you know how. And here it is.
<#@ template language="C#" #>
<#@ output extension="vb" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="$(SolutionDir)/ConsoleApplication1/bin/Debug/ConsoleApplication1.exe" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="ConsoleApplication1.Test" #>
<#@ import namespace="System.Collections.Generic" #>
<#
var namespaces = new List<String>();
namespaces.Add("Test");
namespaces.Add("Test2");
string relativeOutputFilePath = "";
foreach(String _namespace in namespaces)
{ #> ' **************************************
' Generated code, please do not change
' This file was last generated on <#= DateTime.Now #>
' **************************************
Namespace ConsoleApplication1.<#= _namespace #>
''' <summary>
'''
''' </summary>
''' <remarks></remarks>
<CLSCompliant(True)> _
Public Interface IFactory
#Region "Methods"
<# var ty = typeof(Interface3);
var assem = ty.Assembly;
var types = from e in assem.GetTypes() where e.IsInterface && e.Namespace == "ConsoleApplication1." + _namespace select e;
if( types.Count() > 0)
{
foreach(var cls in types)
{
var _FunctionName = cls.Name.Substring(1);
#>
''' <summary>
'''
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Function <#=_FunctionName#>() As <#=cls.Name#>
<#
}
}
#>
#End Region
End Interface
End Namespace
<#
if(!System.IO.Directory.Exists(".../ConsoleApplication1/" + _namespace + "/Factory"))
{
System.IO.Directory.CreateDirectory(".../ConsoleApplication1/" + _namespace + "/Factory");
}
relativeOutputFilePath = ".../ConsoleApplication1/" + _namespace + "/Factory/IFactory.cs";
WriteTemplateOutputToFile(relativeOutputFilePath);
}
#>
<#+
void WriteTemplateOutputToFile(string relativeOutputFilePath)
{
string outputFilePath = relativeOutputFilePath;
System.IO.File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString());
this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
}
#>```
As you can see, I added a List with namespaces, I could have just gotten them from the assembly as well. And then I have a for each which makes a template for each of those namespaces. In the end of that loop, I call WriteTemplateOutputToFile with the desired path. And now comes the important bit.
void WriteTemplateOutputToFile(string relativeOutputFilePath) { string outputFilePath = relativeOutputFilePath; System.IO.File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString()); this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length); }``` The bit of code above writes the generated code to the filesystem and clears the Generated code so that you get a blank slate to generate the next file. I also added a comment at the top of the file to warn users not to change the file since it is generated and changes will get lost when regenerated.
Conclusion
Why write code if you can have it generated? Lazy people make good programmers and even better DBA’s.
Adding a warning to your generated code is also important so that other people don’t change it just to find out they their changes don’t stick.