1.7. Querying Configuration Files with LINQ

Problem

Sets of data can be stored in many different locations, such as configuration files. You want to be able to query your configuration files for sets of information.

Solution

Use LINQ to query against the configuration sections. In the example below, this is done by retrieving all chapter titles with even numbers and the word "and" in the title from the custom configuration section containing chapter information:

	CSharpRecipesConfigurationSection recipeConfig =
	    ConfigurationManager.GetSection("CSharpRecipesConfiguration") as
	CSharpRecipesConfigurationSection;

	var expr = from ChapterConfigurationElement chapter in
	               recipeConfig.Chapters.OfType<ChapterConfigurationElement>()
	           where (chapter.Title.Contains("and")) && ((int.Parse(chapter.Number) % 2)
	== 0)
	           select new
	           {
	               ChapterNumber = "Chapter " + chapter.Number,
	               chapter.Title
	           };

	foreach (var chapterInfo in expr)
	{
	    Console.WriteLine(chapterInfo.ChapterNumber + ": " + chapterInfo.Title);
	}

The configuration section being queried looks like this:

	<CSharpRecipesConfiguration CurrentEdition="3">
	  <Chapters>
	    <add Number="1" Title="Language Integrated Query (LINQ)"/>
	    <add Number="2" Title="Strings and Characters"/>
	    <add Number="3" Title="Classes and Structures"/>
	    <add Number="4" Title="Generics"/>
	    <add Number="5" Title="Collections"/>
	    <add Number="6" Title="Iterators, Partial Types and Partial Methods"/>
	    <add Number="7" Title="Exception Handling"/>
	    <add Number="8" Title="Diagnostics"/>
	    <add Number="9" Title="Delegates, Events, and Functional Programming"/>
	    <add Number="10" Title="Regular Expressions"/>
	    <add Number="11" Title="Data Structures &amp; Algorithms"/>
	    <add Number="12" Title="Filesystem I/O"/>
	    <add Number="13" Title="Reflection"/>
	    <add Number="14" Title="Web"/>
	    <add Number="15" Title="XML"/>
	    <add Number="16" Title="Networking"/>
	    <add Number="17" Title="Security"/>
	    <add Number="18" Title="Threading and Synchronization"/>
	    <add Number="19" Title="Toolbox"/>
	    <add Number="20" Title="Numbers &amp; Enumerations"/>
	  </Chapters>
	  <Editions>
	    <add Number="1" PublicationYear="2004"/>
	    <add Number="2" PublicationYear="2006"/>
	    <add Number="3" PublicationYear="2007"/>
	  </Editions>
	</CSharpRecipesConfiguration>

The output from the query is:

	Chapter 2: Strings and Characters
	Chapter 6: Iterators, Partial Types and Partial Methods
	Chapter 18: Threading and Synchronization

Discussion

Configuration files in .NET play a significant role in achieving manageability and ease of deployment for .NET-based applications. It can be challenging to get all of the various settings right in the hierarchy of configuration files that can affect an application, so understanding how to write utilities to programmatically check configuration file settings is of great use during development, testing, deployment, and ongoing management of an application.

Tip

To access the configuration types, you will need to reference the System.Configuration assembly.

Even though the ConfigurationElementCollection class (the base of sets of data inconfiguration files) only supports IEnumerable and not IEnumerable<T>, we can stilluse it to get the elements we need by using the OfType<ChapterConfigurationElement> method on the collection, which selects elements of that type from the collection:

	var expr = from ChapterConfigurationElement chapter in
	               recipeConfig.Chapters.OfType<ChapterConfigurationElement>()

ChapterConfigurationElement is a custom configuration section class that holds the chapter number and title:

	/// <summary>
	/// Holds the information about a chapter in the configuration file
	/// </summary>
	public class ChapterConfigurationElement : ConfigurationElement
	{
	    /// <summary>
	    /// Default constructor
	    /// </summary>
	    public ChapterConfigurationElement()
	    {
	    }

	    /// <summary>
	    /// The number of the Chapter
	    /// </summary>
	    [ConfigurationProperty("Number", IsRequired=true)]
	    public string Number
	    {
	        get { return (string)this["Number"]; }
	        set { this["Number"] = value; }
	    }

	    /// <summary>
	    /// The title of the Chapter
	    /// </summary>
	    [ConfigurationProperty("Title", IsRequired=true)]
	    public string Title
	    {
	        get { return (string)this["Title"]; }
	        set { this["Title"] = value; }
	    }
	}

This technique can be used on the standard configuration files such as machine.config as well. This example determines which sections in machine.config require access permissions. For this collection, OfType<ConfigurationSection> is used, as this is a standard section:

	System.Configuration.Configuration machineConfig =
	    ConfigurationManager.OpenMachineConfiguration();

	var query = from ConfigurationSection section in machineConfig.Sections.
	OfType<ConfigurationSection>()
	            where section.SectionInformation.RequirePermission
	            select section;

	foreach (ConfigurationSection section in query)
	{
	    Console.WriteLine(section.SectionInformation.Name);
	}

The sections detected will look something like this:

	system.data
	windows
	system.webServer
	mscorlib
	system.data.oledb
	system.data.oracleclient
	system.data.sqlclient
	configProtectedData
	satelliteassemblies
	system.data.dataset
	startup
	system.data.odbc
	system.diagnostics
	runtime
	system.codedom
	system.runtime.remoting
	assemblyBinding
	system.windows.forms

See Also

The "Enumerable.OfType, method," "ConfigurationSectionCollection,class" and "ConfigurationElementCollection class" topics in the MSDN documentation.

Get C# 3.0 Cookbook, 3rd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.