Code blocks in a T4 template are run in the context of the templating service and have no notion of app/web config.
Oleg Sych provides much detail on T4 Architecture.
To access configuration files in a template we have to drill down a few levels of abstraction to get a reference to the project hosting the template. From that point we can utilize classes from the Configuration namespace to closely approximate standard Configuration file behavior.
Presented here is a re-usable template, ConfigurationAccessor.tt, that can be included in a template to provide strongly typed access to the configuration file and hosting project, and a sample of usage. Simple add the files to a project and then right-click ConfigurationAccessorUsageExample.tt and select context menu item 'Run Custom Tool'.
Implementation details are in the code.
ConfigurationAccessor.tt
<#@ assembly name="><#@ assembly name="System.Configuration" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#+
/// <summary>
/// Provides strongly typed access to the hosting EnvDTE.Project and app.config/web.config
/// configuration file, if present.
///
/// Typical usage from T4 template:
/// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
///
/// </summary>
/// <author>Sky Sanders [sky.sanders@gmail.com, http://skysanders.net/subtext]</author>
/// <date>01-23-10</date>
/// <copyright>The contents of this file are a Public Domain Dedication.</copyright>
///
/// TODO: determine behaviour of ProjectItem.FileNames when referred to a linked file.
///
public class ConfigurationAccessor
{
/// <summary>
/// Typical usage from T4 template:
/// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
/// </summary>
public ConfigurationAccessor(IServiceProvider host)
{
// Get the instance of Visual Studio that is hosting the calling file
EnvDTE.DTE env = (EnvDTE.DTE)host.GetService(typeof(EnvDTE.DTE));
// Gets an array of currently selected projects. Since you are either in this file saving it or
// right-clicking the item in solution explorer to invoke the context menu it stands to reason
// that there is 1 ActiveSolutionProject and that it is the parent of this file....
_project = (EnvDTE.Project)((Array)env.ActiveSolutionProjects).GetValue(0);
string configurationFilename=null;
// examine each project item's filename looking for app.config or web.config
foreach (EnvDTE.ProjectItem item in _project.ProjectItems)
{
if (Regex.IsMatch(item.Name,"(app|web).config",RegexOptions.IgnoreCase))
{
// TODO: try this with linked files. is the filename pointing to the source?
configurationFilename=item.get_FileNames(0);
break;
}
}
if(!string.IsNullOrEmpty(configurationFilename))
{
// found it, map it and expose salient members as properties
ExeConfigurationFileMap configFile = null;
configFile = new ExeConfigurationFileMap();
configFile.ExeConfigFilename=configurationFilename;
_configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
}
}
private EnvDTE.Project _project;
private Configuration _configuration;
/// <summary>
/// Provides access to the host project.
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project.aspx</remarks>
public EnvDTE.Project Project
{
get { return _project; }
}
/// <summary>
/// Convenience getter for Project.Properties.
/// Examples:
/// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
/// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project_properties.aspx</remarks>
public EnvDTE.Properties Properties
{
get { return _project.Properties;}
}
/// <summary>
/// Provides access to the application/web configuration file.
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.aspx</remarks>
public Configuration Configuration
{
get { return _configuration; }
}
/// <summary>
/// Provides access to the appSettings section of the configuration file.
/// Behavior differs from typical AppSettings usage in that the indexed
/// item's .Value must be explicitly addressed.
/// <code>string setting = config.AppSettings["MyAppSetting"].Value;</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.appsettings.aspx</remarks>
public KeyValueConfigurationCollection AppSettings
{
get { return _configuration.AppSettings.Settings;}
}
/// <summary>
/// Provides access to the connectionStrings section of the configuration file.
/// Behavior is as expected; items are accessed by string key or integer index.
/// <code>string northwindProvider = config.ConnectionStrings["northwind"].ProviderName;</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.connectionstrings.aspx</remarks>
public ConnectionStringSettingsCollection ConnectionStrings
{
get { return _configuration.ConnectionStrings.ConnectionStrings;}
}
}
#>
ConfigurationAccessorUsageExample.tt
<#@ template language=" debug="true" hostspecific="true"><#@ output extension="txt" #>
<#@ include file="ConfigurationAccessor.tt" #>
<#
/// <summary>
/// Demonstrates usage of ConfigurationAccessor.tt
/// </summary>
/// <author>Sky Sanders [sky.sanders@gmail.com, http://skysanders.net/subtext]</author>
/// <date>01-23-10</date>
/// <copyright>The contents of this file are a Public Domain Dedication.</copyright>
///
// instantiate ConfigurationAccessor
var config = new ConfigurationAccessor((IServiceProvider)this.Host);
// T4 TIP: work around the 'class feature' constrictions by using anonymous methods
Action<string> header=new Action<string>((s)=>
{
this.WriteLine("");
this.WriteLine("--------------------------------------------------------------");
this.WriteLine(s);
this.WriteLine("--------------------------------------------------------------");
});
// ----------------------------------------
// Relevant ConfigurationAccessor Members
// ----------------------------------------
header("ConfigurationAccessor.Project");
// Project provides access to the EnvDTE.Project that hosts this file.
// see http://msdn.microsoft.com/en-us/library/envdte.project.aspx
this.WriteLine("Project.FileName = {0}",config.Project.FileName);
this.WriteLine("Project.FullName = {0}",config.Project.FullName);
this.WriteLine("Project.Name = {0}",config.Project.Name);
header("ConfigurationAccessor.Properties");
// Properties is a convenience accessor to Project.Properties
// see http://msdn.microsoft.com/en-us/library/envdte.project_properties.aspx
// enumerate all properties exposed by this project
var en = config.Properties.GetEnumerator();
while(en.MoveNext())
{
var property = (EnvDTE.Property)en.Current;
object propertyValue = null;
try
{
propertyValue = property.Value;
}
catch (Exception ex)
{
propertyValue = ex.Message;
}
this.WriteLine("{0} = {1}",property.Name,propertyValue.ToString());
}
header("ConfigurationAccessor.Configuration");
// Configuration provides access the app/web config.
// Use the methods on this member to gain access to arbitrary/custom configuration sections.
// see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.aspx
var appSettingsSection = (AppSettingsSection)config.Configuration.GetSection("appSettings");
this.WriteLine(appSettingsSection.Settings["MyAppSetting"].Value);
header("ConfigurationAccessor.AppSettings");
// AppSettings provides access to the AppSettings section.
// NOTE: you must explicitly access the Value property of the item.
// see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.appsettings.aspx
this.WriteLine("\r\nString key access\r\nMyAppSetting = {0}\r\n",config.AppSettings["MyAppSetting"].Value);
this.WriteLine("Enumerator access\r\n");
en = config.AppSettings.GetEnumerator();
while(en.MoveNext())
{
var kv = (KeyValueConfigurationElement)en.Current;
this.WriteLine("{0} = {1}",kv.Key,kv.Value);
}
header("ConfigurationAccessor.ConnectionStrings");
// ConnectionStrings behaves as expected.
// see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.connectionstrings.aspx
this.WriteLine("\r\nString key access\r\nnorthwind = {0}\r\n",config.ConnectionStrings["northwind"].ConnectionString);
this.WriteLine("Enumerator access\r\n");
en = config.ConnectionStrings.GetEnumerator();
while(en.MoveNext())
{
var cs = (ConnectionStringSettings)en.Current;
this.WriteLine("{0}, {1}, {2}",cs.Name,cs.ProviderName,cs.ConnectionString);
}
#>
app.config
<?xml version=" encoding="utf-8"><configuration>
<appSettings>
<add key="MyAppSetting" value="MyAppSettingValue"/>
</appSettings>
<connectionStrings>
<add name="northwind" providerName="System.Data.SqlClient" connectionString="Data Source=.\sqlexpress;Initial Catalog=Northwind;Integrated Security=True"/>
</connectionStrings>
</configuration>
CodeProject
Technorati tags:
T4,
CodeGen