views:

1511

answers:

4

I have a dll that I want to read from a manually specified app.config file (the dll is an .net extension to a native com dll that is a Microsoft Management Console snap in, so there is no mmc.exe.config). I have been able to open the config file, read the relevant group and section to get the setting that I want. Like this:

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            fileMap.ExeConfigFilename = Assembly.GetExecutingAssembly().Location + ".config"; 
            Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
            ShowSectionGroupCollectionInfo(config.SectionGroups);
            ConfigurationSectionGroup group = config.SectionGroups["applicationSettings"];
            ClientSettingsSection section = group.Sections["Namespace.Properties.Settings"] as ClientSettingsSection;
            SettingElement sectionElement = section.Settings.Get("AllowedPlugins");

            SettingValueElement elementValue = sectionElement.Value;

The settings are a string collection and a string. like so:

<applicationSettings>
    <Namespace.Properties.Settings>
        <setting name="AllowedPlugins" serializeAs="Xml">
            <value>
                <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
                    <string>Plugin1.Name</string>
                    <string>Plugin2.Name</string>
                    <string>Plugin3.Name</string>
                </ArrayOfString>
            </value>
        </setting>
        <setting name="blah" serializeAs="String">
            <value>sajksjaksj</value>
        </setting>
    </Namespace.Properties.Settings>
</applicationSettings>

I can create a string array from this in a bit of a kak handed way:

List<String> values = new List<string>(elementValue.ValueXml.InnerText.Split(new string[]{" ",Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries ));

but I wonder if there is a nice way that I'm missing that I can get my settings to be read and converted into objects of the right type in the same way that they are when the standard app.config file is read.

Please tell me there is...

+5  A: 

The way that I've done this in the past is use a custom configuration section and implement an IConfigurationSectionHandler for the section. You still have to do all of the parsing inside the configuration section handler, but it makes it easy to get the information in the configuration section in the form you want it in your application. There is a How To article on MSDN that goes through the process of creating such a handler.

Below is an example of one that I've used:

usage:

Dictionary<string,AdministrativeRole> roles
        = ConfigurationManager.GetSection("roles")
             as Dictionary<string,AdministrativeRole>;

web.config:

<configuration>
   <configSections>
      <section name="roles"
               type="com.example.RolesDefinitionHandler, com.example" />
   </configSections>
   <roles>
       <role name="Administrator" group="domain\group-name" />
       ...
   </roles>
</configuration>

code:

public class RolesDefinitionHandler : IConfigurationSectionHandler
{
    private static readonly string ROLE_SECTION_NAME = "role";
    private static readonly string ROLE_SECTION_NAME_ATTRIBUTE = "name";
    private static readonly string ROLE_SECTION_GROUP_ATTRIBUTE = "group";

    private Dictionary<string, AdministrativeRole> ReadConfiguration( XmlNode section )
    {
        Dictionary<string, AdministrativeRole> roles = new Dictionary<string, AdministrativeRole>();
        foreach (XmlNode node in section.ChildNodes)
        {
            if (node.Name.Equals( ROLE_SECTION_NAME, StringComparison.InvariantCultureIgnoreCase ))
            {
                string name = (node.Attributes[ROLE_SECTION_NAME_ATTRIBUTE] != null) ? node.Attributes[ROLE_SECTION_NAME_ATTRIBUTE].Value.ToLower() : null;
                if (string.IsNullOrEmpty( name ))
                {
                    throw new ConfigurationErrorsException( "missing required attribute " + ROLE_SECTION_NAME_ATTRIBUTE );
                }

                string group = (node.Attributes[ROLE_SECTION_GROUP_ATTRIBUTE] != null) ? node.Attributes[ROLE_SECTION_GROUP_ATTRIBUTE].Value.ToLower() : null;
                if (string.IsNullOrEmpty( group ))
                {
                    throw new ConfigurationErrorsException( "missing required attribute " + ROLE_SECTION_GROUP_ATTRIBUTE );
                }

                if (roles.ContainsKey( name ))
                {
                    throw new ConfigurationErrorsException( "duplicate " + ROLE_SECTION_NAME + " for " + name );
                }

                roles.Add( name, new AdministrativeRole( name, group ) );
            }
            else
            {
                throw new ConfigurationErrorsException( "illegal node " + node.Name );
            }
        }

        return roles;
    }

    #region IConfigurationSectionHandler Members

    public object Create( object parent, object configContext, System.Xml.XmlNode section )
    {
        return ReadConfiguration( section );
    }

    #endregion
}
tvanfosson
thanks for that. This seems like overkill for type that are already parsable by the default. I could understand doing this if I was sticking my own types in the config, but this is a standard type of SpecializedStringCollection. Surely I don't need to do this for that? I'll look into it anyway
Sam Holder
A: 

Hey tvanfosson, Thanks for really good idea... :)

A: 

A better solution might be, which is similar to how sql connection strings are added to a app.config:

http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx

A: 

I thought IConfigurationSectionHandler was deprecated!

Yazid