Salient Solutions

wrasslin ones and nones for fun and profit - Sky Sanders' Blog
posts - 96, comments - 70, trackbacks - 0

Accessing app.config/web.config from T4 template

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>

 

 


Technorati tags:

Print | posted on Saturday, January 23, 2010 10:29 AM |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 7 and 1 and type the answer here:

Powered by: