| I think the IConfigurationHandler is a wonderful way to provide the kind
of
| extensibility you are requiring.
Using IConfigurationHandler is a "wonderful" way of doing it, if you like
flexibility at the expense of writing lots & lots of code. (as compared to
the .NET 2.0 way of doing it).
You should check out what's new in .NET 2.0 System.Configuration namespace!
http://msdn2.microsoft.com/en-us/lib...iguration.aspx
You can have very rich & type safe configuration class simply by inheriting
from ConfigurationElement, ConfigurationSection or
ConfigurationSectionGroup. Adding some properties with attributes; the
attributes control the name of the element as well as validation...
Something like:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="widgets" type="Test.Configuration.WidgetsSection, Test"
/>
</configSections>
<widgets>
<add name="Widget1" type="Test.Widget, Test" />
<add name="Widget2" type="Test.Widget, Test" />
</widgets>
</configuration>
Option Strict On
Option Explicit On
' references the System.Configuration assembly
Imports System.Configuration
Imports System.ComponentModel
Namespace Configuration
Public Class WidgetsSection
Inherits ConfigurationSection
Private Const WidgetsElementName As String = ""
<ConfigurationProperty(WidgetsElementName,
IsDefaultCollection:=True)_
<ConfigurationCollection(GetType(WidgetSettings) )_
Public ReadOnly Property Widgets() As WidgetSettingsCollection
Get
Return DirectCast(MyBase.Item(WidgetsElementName),
WidgetSettingsCollection)
End Get
End Property
End Class
Public NotInheritable Class WidgetSettings
Inherits ConfigurationElement
Private Const NameProperty As String = "name"
Private Const TypeProperty As String = "type"
Public Sub New()
End Sub
Public Sub New(ByVal elementName As String)
Name = elementName
End Sub
<ConfigurationProperty(NameProperty, IsKey:=True, IsRequired:=True,
DefaultValue:="Name")_
<StringValidator(MinLength:=1)_
Public Property Name() As String
Get
Return DirectCast(Me(NameProperty), String)
End Get
Set(ByVal value As String)
Me(NameProperty) = value
End Set
End Property
<ConfigurationProperty(TypeProperty, IsRequired:=True)_
<TypeConverter(GetType(TypeNameConverter))_
<SubclassTypeValidator(GetType(Widget))_
Public Property Type() As Type
Get
Return DirectCast(Me(TypeProperty), Type)
End Get
Set(ByVal value As Type)
Me(TypeProperty) = value
End Set
End Property
End Class
Public NotInheritable Class WidgetSettingsCollection
Inherits ConfigurationElementCollection(Of WidgetSettings)
Public Sub New()
MyBase.New(StringComparer.CurrentCultureIgnoreCase )
End Sub
Protected Overloads Overrides Function CreateNewElement() As
ConfigurationElement
Return New WidgetSettings()
End Function
Protected Overrides Function CreateNewElement(ByVal elementName As
String) As ConfigurationElement
Return New WidgetSettings(elementName)
End Function
Protected Overrides Function GetElementKey(ByVal element As
ConfigurationElement) As Object
Return DirectCast(element, WidgetSettings).Name
End Function
End Class
Public MustInherit Class ConfigurationElementCollection(Of T As
ConfigurationElement)
Inherits ConfigurationElementCollection
Protected Sub New()
End Sub
Protected Sub New(ByVal comparer As IComparer)
MyBase.New(comparer)
End Sub
Default Public Overloads Property Item(ByVal index As Integer) As T
Get
Return DirectCast(Me(index), T)
End Get
Set(ByVal value As T)
Me(index) = value
End Set
End Property
Default Public Overloads Property Item(ByVal name As String) As T
Get
Return DirectCast(Me(name), T)
End Get
Set(ByVal value As T)
Me(name) = value
End Set
End Property
Public Sub Add(ByVal settings As T)
BaseAdd(settings)
End Sub
Public Sub Clear()
BaseClear()
End Sub
Public Sub Remove(ByVal name As String)
BaseRemove(name)
End Sub
Public Sub Remove(ByVal settings As T)
BaseRemove(GetElementKey(settings))
End Sub
Public Sub RemoveAt(ByVal index As Integer)
BaseRemoveAt(index)
End Sub
Public Function IndexOf(ByVal settings As T) As Integer
Return BaseIndexOf(settings)
End Function
Protected Overrides Sub BaseAdd(ByVal index As Integer, ByVal
element As ConfigurationElement)
MyBase.BaseAdd(index, element)
End Sub
End Class
End Namespace
FWIW: ConfigurationElementCollection(Of T) class encapsulates the common
ConfigurationElementCollection logic in a typesafe type.
To really see the .NET 2.0 configuration in action check out the source to
Enterprise Library 2.0.
http://www.gotdotnet.com/codegallery...2-91be63527327
--
Hope this helps
Jay B. Harlow [MVP - Outlook]
..NET Application Architect, Enthusiast, & Evangelist
T.S. Bradley -
http://www.tsbradley.net
"Jared" <Ja***@discussions.microsoft.comwrote in message
news:F3**********************************@microsof t.com...
| What do you mean by "how would the framework know what my object looks
like"?
|
| You are in total control; for instance, you can create a strongly typed
| object that represents your configuration section. Make sure you provide
| support for serialization and just deserialize it into your type. Return
in
| the Create method.
| Then all you need to do is provide a property accessor (possibly through a
| singleton instance) in your library/app and your job is done. Now you
have a
| strongly typed representation of your object for all to use.
|
| I think the IConfigurationHandler is a wonderful way to provide the kind
of
| extensibility you are requiring. To provide the same results in you own
code
| you would expect to write more code, leaving the chance that more can go
| wrong. I.E. You are not concerned with IO, creating the document, and/or
| xpathing to the section. If you implement your object using the
| xmlserializer then you have about 10 lines of code to write, all of which
are
| native framework functions. You can't really count the xml in the config
| file, which would need to be done regardless of the option you choose.
| Finally, the property accessor, this should be something you are already
used
| to, its very poor design to allow consumers to access your member data
| directly.
|
| The following example should illustrate my point. Also, one of the big
| advantages of this is that it allows you to make your modifications in a
| single place - the class that you are extending (I'm not referring to the
| consumers of the class, that is inevitable).
|
| I'm curious to see what your (and others) thoughts are on this subject.
|
| Jared
|
| private static void ConfigExample()
| {
| MyNamespace.MyCustomObject customObject =
| MyNamespace.MyCustomObject.Instance;
|
| foreach (MyNamespace.MyCustomObject.MyItem item in customObject.Items)
| {
| Console.WriteLine("{0} = {1}", item.Key, item.Value);
| }
| }
|
|
| namespace MyNamespace
| {
| public class MySectionHandler :
| System.Configuration.IConfigurationSectionHandler
| {
| #region IConfigurationSectionHandler Members
|
| public object Create(object parent, object configContext, XmlNode
| section)
| {
| System.IO.MemoryStream mem = new MemoryStream();
| XmlTextWriter writer = new XmlTextWriter(mem, null);
| XmlSerializer ser = new XmlSerializer(typeof(MyCustomObject));
| section.WriteTo(writer);
| writer.Flush();
| mem.Seek(0, SeekOrigin.Begin);
| MyCustomObject customObject = ser.Deserialize(mem) as
| MyCustomObject;
| writer.Close();
| return customObject;
| }
|
| #endregion
| }
| }
|
| namespace MyNamespace
| {
| [Serializable(), XmlRoot("MySection")]
| public sealed class MyCustomObject
| {
| static MyCustomObject()
| {
| lockObject = new object();
| }
| private MyCustomObject()
| {
| _items = (MyItem[])Array.CreateInstance(typeof(MyItem), 0);
| _items.Initialize();
| }
|
| private MyItem[] _items;
| private static MyCustomObject _instance;
| private static readonly object lockObject;
|
| public static MyCustomObject Instance
| {
| get
| {
| lock (lockObject)
| {
| try
| {
| _instance =
| System.Configuration.ConfigurationSettings.GetConf ig("MySection") as
| MyCustomObject;
| }
| catch (Exception){}
| if (_instance == null)
| {
| _instance = new MyCustomObject();
| }
| return _instance;
| }
| }
| }
|
| [XmlElement("add")]
| public MyItem[] Items
| {
| get { return _items; }
| set { _items = value; }
| }
|
| [Serializable()]
| public class MyItem
| {
| public MyItem()
| {
| Key = string.Empty;
| Value = string.Empty;
| }
| private string _key;
| private string _value;
| [XmlAttribute("key")]
| public string Key
| {
| get { return _key; }
| set
| {
| if (value == null)
| {
| _key = string.Empty;
| return;
| }
| _key = value;
| }
| }
| [XmlAttribute("value")]
| public string Value
| {
| get { return _value; }
| set
| {
| if (value == null)
| {
| _value = string.Empty;
| return;
| }
| _value = value;
| }
| }
| }
|
| }
| }
|
| "Sathyaish" wrote:
|
| Thanks, Jared. Implementing the IConfigurationSectionHandler.Create()
| is the sad part. It is like Microsoft saying, "Look! It is very easy.
| Just do everything yourself and we'll give you the chance of putting
| your own sweet, custom, nice-looking sections in our App.Config. And
| that way, you get total control over your configuration file. Just
| remember to do all the legwork."
| >
| But again, how would the framework know what my object looks like.
| >
| >
| Thanks.
| >
| >