As I was writing a small TCP server for serving Silverlight local TCP policy I came across a certain need. Inspired by Dan Wahlin's server implementation I chose to write a simplified version for myself. I needed to keep some XML in App.config without constraining it with a schema.
The normal solution in this case is a custom section, sibling to appSettings if you wish. So my App.Config looked at first like this :
<configuration>
<appSettings>
<add key="ipAddress" value="127.0.0.1"/>
</appSettings>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*" />
</allow-from>
<grant-to>
<socket-resource port="4502" protocol="tcp" />
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
</configuration>
Upon running the program even addressing the "ipAddress" key in the appSettings section throws an exception like:
System.Configuration.ConfigurationErrorsException was unhandled
Message="Configuration system failed to initialize"
Source="System.Configuration"
BareMessage="Configuration system failed to initialize"
Line=0
StackTrace:
at System.Configuration.ConfigurationManager.PrepareConfigSystem()
at System.Configuration.ConfigurationManager.GetSection(String sectionName)
at System.Configuration.ConfigurationManager.get_AppSettings()
at ConsoleApplication1.Program.Main(String[] args) in C:\Users\Andrei\Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs:line 21
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.Configuration.ConfigurationErrorsException
Message="Unrecognized configuration section access-policy. (C:\\Users\\Andrei\\Documents\\Visual Studio 2008\\Projects\\ConsoleApplication1\\ConsoleApplication1\\bin\\Debug\\ConsoleApplication1.vshost.exe.config line 8)"
Source="System.Configuration"
BareMessage="Unrecognized configuration section access-policy."
Filename="C:\\Users\\Andrei\\Documents\\Visual Studio 2008\\Projects\\ConsoleApplication1\\ConsoleApplication1\\bin\\Debug\\ConsoleApplication1.vshost.exe.config"
Line=8
StackTrace:
at System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
at System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors)
at System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
InnerException:
So something is wrong. We need to tell the runtime that the "access-policy" section is allowed.
<configuration>
<configSections>
<section name="access-policy" type="CustomSections.InlineXmlSection, CustomSections"/>
</configSections>
...
At first I haven't placed the type attribute in the "section" element but it turned out it had to specified and be not empty. Moreover it must containt the fully-qualified class name and the assembly which contains it. The class must inherit from System.Configuration.ConfigurationSection.
So I created an assembly called CustomSections, added references to the System.Configuration assembly and the System.Xml assembly.
All you need to do is override the DeserializeSection method and load the XML document in there :
using System.Configuration;
using System.Xml;
namespace CustomSections
{
public class InlineXmlSection : ConfigurationSection
{
public XmlDocument Content { get; private set; }
protected override void DeserializeSection(XmlReader reader)
{
(this.Content = new XmlDocument()).Load(reader);
}
}
}
The code is pretty self-explanatory : we instantiate a new XmlDocument and load it from the XmlReader provided to us by the configuration infrastructure. If anything goes bad the exception handling will be the responsability of the caller. In this case the first call to ConfigurationManager.
Now let's put the code to use :
private static void Main(string[] args)
{
expectedRequestBytes = Encoding.UTF8.GetBytes("<policy-file-request/>");
listener = new TcpListener(IPAddress.Parse(ConfigurationManager.AppSettings["ipAddress"]), 943);
var policySection = (InlineXmlSection)ConfigurationManager.GetSection("access-policy");
policyBytes = Encoding.UTF8.GetBytes(policySection.Content.OuterXml);
...
The underlined code is the relevant portion (the rest is provided for context). We get the section via ConfigurationManager.GetSection and we have to cast the result to the desired section type. The we use the section as we see fit.
I will use this code sample in a future blog post regarding writing a very small TCP server for serving Silverlight 943 TCP Port policy XML content. Hope you find this useful.