As I was writing a small TCP server for serving a 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 the 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>
<strong>
<configSections>
<section name="access-policy" type="CustomSections.InlineXmlSection, CustomSections"/>
</configSections>
</strong>
...
At first, I didn’t place the type attribute in the “section” element, but it turned out it had to be specified and not be empty. Moreover, it must contain 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, and 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 responsibility 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. Then we use the section as we see fit.
Recent Comments