I've searched the site and haven't found exactly what I'm looking for. Close, but no cigar.
Basically I want to have a config section like this:
<configSections>
<section name="PhoneNotificationsSection" type="Alerts.PhoneAlertConfigSection,Alerts,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
</configSections>
<PhoneNotificationsSection>
<phones>
<add phone="MyMobile" value="1234567890@vtext.com" />
<add phone="OtherMobile" value="1234567890@txt.att.com" />
</phones>
</PhoneNotificationsSection>
Then I'd like to, in my appSettings consuming code, be able to write something like this (pseudo code):
foreach (phone p in phones)
{
//'phone' attribute is just helpful/descriptive
DoSomething(p.value);
}
I've done enough research to know I probably need a few of my own classes that implement and/or inherit from certain Configuration classes to make the above code possible. I just haven't found anything that clearly demonstrates this scenario and how to code for it - and when I try to learn the whole .NET configuration world my brain starts to hurt. Anyone have some code like what I'm looking for that they can share?
I've written something similar once, as an example for a C# course. In my opinion it mainly demonstrates how awful the .NET configuration subsystem is, although the code does work. I've not adapted it to your settings, as it's fairly easy to introduce a mistake and so far the SO editor does not validate posted code samples ;)
First, the configuration section declaration:
<configSections>
<section name="passwordSafe"
type="Codeworks.PasswordSafe.Model.Configuration.PasswordSafeSection, Codeworks.PasswordSafe.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</configSections>
<passwordSafe hashAlgorithm="SHA256">
<users>
<user name="mm" password="Jok2eyBcFs4y7UIAlCuLix4mLfxw2byfvHfElpmk8d8=" />
<user name="joe" password="Jok2eyBcFs4y7UIAlCuLix4mLfxw2byfvHfElpmk8d8=" />
</users>
</passwordSafe>
To match the above snippet we first need the configuration section:
public class PasswordSafeSection : ConfigurationSection
{
#region Static Accessors
/// <summary>
/// Gets the configuration section using the default element name.
/// </summary>
public static PasswordSafeSection GetSection()
{
return GetSection( "passwordSafe" );
}
/// <summary>
/// Gets the configuration section using the specified element name.
/// </summary>
public static PasswordSafeSection GetSection( string sectionName )
{
PasswordSafeSection section = ConfigurationManager.GetSection( sectionName ) as PasswordSafeSection;
if( section == null )
{
string message = string.Format( "The specified configuration section (<{0}>) was not found.", sectionName );
throw new ConfigurationErrorsException( message );
}
return section;
}
#endregion
#region Configuration Properties
[ConfigurationProperty( "hashAlgorithm" )]
public string HashAlgorithm
{
get { return (string) this[ "hashAlgorithm" ]; }
set { this[ "hashAlgorithm" ] = value; }
}
[ConfigurationProperty( "users", IsDefaultCollection=true )]
public UserElementCollection Users
{
get { return (UserElementCollection) this[ "users" ]; }
set { this[ "users" ] = value; }
}
public override bool IsReadOnly()
{
return false;
}
#endregion
}
We are using a custom element collection, so let's declare that too:
[ConfigurationCollection( typeof(UserElement), CollectionType = ConfigurationElementCollectionType.BasicMap )]
public class UserElementCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new UserElement();
}
protected override string ElementName
{
get { return "user"; }
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
public override bool IsReadOnly()
{
return false;
}
#region Indexers
public UserElement this[ int index ]
{
get { return BaseGet( index ) as UserElement; }
set
{
if( BaseGet( index ) != null )
{
BaseRemoveAt( index );
}
BaseAdd( index, value );
}
}
public new UserElement this[ string name ]
{
get { return BaseGet( name ) as UserElement; }
}
#endregion
#region Lookup Methods
protected override object GetElementKey( ConfigurationElement element )
{
UserElement user = element as UserElement;
return user != null ? user.UserName : "error";
}
public string GetKey( int index )
{
return (string) BaseGetKey( index );
}
#endregion
#region Add/Remove/Clear Methods
public void Add( UserElement item )
{
BaseAdd( item );
}
public void Remove( string name )
{
BaseRemove( name );
}
public void Remove( UserElement item )
{
BaseRemove( GetElementKey( item ) );
}
public void RemoveAt( int index )
{
BaseRemoveAt( index );
}
public void Clear()
{
BaseClear();
}
#endregion
}
And finally we need to declare the custom element used in the element collection:
public class UserElement : ConfigurationElement
{
#region Constructors
public UserElement()
{
}
public UserElement( string userName, string passwordHash )
{
UserName = userName;
PasswordHash = passwordHash;
}
#endregion
#region Configuration Properties
[ConfigurationProperty( "name", IsKey = true )]
public string UserName
{
get { return (string) this[ "name" ]; }
set { this[ "name" ] = value; }
}
[ConfigurationProperty( "password", IsRequired = true )]
public string PasswordHash
{
get { return (string) this[ "password" ]; }
set { this[ "password" ] = value; }
}
public override bool IsReadOnly()
{
return false;
}
#endregion
}
Now, having all this in place we're ready to access the configuration file. I'm using a Configurator helper class to make this slightly less cumbersome:
public static class Configurator
{
#region AppSettings Helpers
public static int SplashScreenDisplayTime
{
get { return Convert.ToInt32( ConfigurationManager.AppSettings[ "splash.display.msecs" ] ); }
}
#endregion
#region User Helpers
public static bool TryGetUserPasswordHash( string userName, out string passwordHash )
{
UserElement user = GetUser( userName );
passwordHash = user != null ? user.PasswordHash : null;
return ! string.IsNullOrEmpty( passwordHash );
}
private static UserElement GetUser( string userName )
{
SystemConfiguration config = GetConfiguration( ConfigurationUserLevel.PerUserRoamingAndLocal );
PasswordSafeSection section = config.Sections[ "passwordSafe" ] as PasswordSafeSection;
return section.Users[ userName ];
}
public static void AddUser( string userName, string passwordHash, string encryptionKey )
{
SystemConfiguration config = GetConfiguration( ConfigurationUserLevel.PerUserRoamingAndLocal );
PasswordSafeSection section = config.Sections[ "passwordSafe" ] as PasswordSafeSection;
UserElement user = section.Users[ userName ];
if( user == null )
{
user = new UserElement( userName, passwordHash, encryptionKey );
section.Users.Add( user );
config.Save( ConfigurationSaveMode.Modified );
}
}
public static void RemoveUser( string userName )
{
SystemConfiguration config = GetConfiguration( ConfigurationUserLevel.PerUserRoamingAndLocal );
PasswordSafeSection section = config.Sections[ "passwordSafe" ] as PasswordSafeSection;
section.Users.Remove( userName );
config.Save( ConfigurationSaveMode.Modified );
}
public static void UpdateUser( string userName, string passwordHash )
{
SystemConfiguration config = GetConfiguration( ConfigurationUserLevel.PerUserRoamingAndLocal );
PasswordSafeSection section = config.Sections[ "passwordSafe" ] as PasswordSafeSection;
UserElement user = section.Users[ userName ];
if( user != null )
{
user.PasswordHash = passwordHash;
config.Save( ConfigurationSaveMode.Modified );
}
}
#endregion
#region Configuration Helpers
private static SystemConfiguration GetConfiguration( ConfigurationUserLevel userLevel )
{
SystemConfiguration config = InitializeConfiguration( userLevel );
return config;
}
private static SystemConfiguration InitializeConfiguration( ConfigurationUserLevel userLevel )
{
SystemConfiguration config = ConfigurationManager.OpenExeConfiguration( userLevel );
PasswordSafeSection section = config.Sections[ "passwordSafe" ] as PasswordSafeSection;
if( section == null )
{
section = new PasswordSafeSection();
section.SectionInformation.AllowExeDefinition = ConfigurationAllowExeDefinition.MachineToLocalUser;
section.SectionInformation.ForceSave = true;
config.Sections.Add( "passwordSafe", section );
config.Save( ConfigurationSaveMode.Full );
}
return config;
}
#endregion
}
Hope this helps.