|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Adding a Section In Configuration FileI know that we can add a single name value entry in app.config or
web.config in the configuration/configSettings/appSettings section like so: <add key="key" value="value" /> Question: I want to add a dictionary of name-value pairs. In other words, instead of a single name-value, I want to add a name-value collection in there. I could do this: <add key="key1" value="value1" /> <add key="key2" value="value2" /> <add key="key3" value="value3" /> but that would mix it up with other members that I do not intend to have as a part of this collection. Is there a way I can add a collection itself? How? And how would I then access it? Water Cooler,
You can create your own configuration section and use one of the staic methods in the System.Configuration namespace to retrieve the information. Attached is a simple example. Also lookup configSection element and IConfigurationSectionHandler interface for additional information. // If you are using 2.0 make the compiler recommend change to the obsolete GetConfig("MySection") method call. /* USE */ private static void ConfigExample() { System.Collections.Specialized.StringDictionary dictionary = (System.Collections.Specialized.StringDictionary) System.Configuration.ConfigurationSettings.GetConfig("MySection"); IEnumerator iterator = dictionary.GetEnumerator(); DictionaryEntry entry; while (iterator.MoveNext()) { entry = (DictionaryEntry)iterator.Current; Console.WriteLine("{0} = {1}", entry.Key, entry.Value); } } /* APP.CONFIG OR WEB.CONFIG */ <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="MySection" type="MyNamespace.MySectionHandler, MyAssemblyName"/> </configSections> <MySection> <add key="key1" value="value1" /> <add key="key2" value="value2" /> <add key="key3" value="value3" /> </MySection> </configuration> /* IConfigurationSectionHandler Implementation */ namespace MyNamespace { public class MySectionHandler : System.Configuration.IConfigurationSectionHandler { #region IConfigurationSectionHandler Members public object Create(object parent, object configContext, XmlNode section) { System.Collections.Specialized.StringDictionary mySection = new System.Collections.Specialized.StringDictionary(); if (section == null || section.ChildNodes.Count == 0) { return mySection; } foreach (XmlNode node in section.ChildNodes) { mySection.Add(node.Attributes[0].Value, node.Attributes[1].Value); } return mySection; } #endregion } } Show quote "Water Cooler v2" wrote: > I know that we can add a single name value entry in app.config or > web.config in the configuration/configSettings/appSettings section like > so: > > <add key="key" value="value" /> > > Question: I want to add a dictionary of name-value pairs. In other > words, instead of a single name-value, I want to add a name-value > collection in there. I could do this: > > <add key="key1" value="value1" /> > <add key="key2" value="value2" /> > <add key="key3" value="value3" /> > > but that would mix it up with other members that I do not intend to > have as a part of this collection. Is there a way I can add a > collection itself? How? And how would I then access it? > > 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. 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.GetConfig("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; } } } } } Show quote "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. > > | I think the IConfigurationHandler is a wonderful way to provide the kind Using IConfigurationHandler is a "wonderful" way of doing it, if you like of | extensibility you are requiring. 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/library/system.configuration.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/codegallery.aspx?id=295a464a-6072-4e25-94e2-91be63527327 -- Show quoteHope this helps Jay B. Harlow [MVP - Outlook] ..NET Application Architect, Enthusiast, & Evangelist T.S. Bradley - http://www.tsbradley.net "Jared" <Ja***@discussions.microsoft.com> wrote in message news:F3CC197E-993A-4888-A594-2DB45B7E0B9A@microsoft.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.GetConfig("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. | > | > Jay,
Thanks! I wasn't aware of all the changes to the System.Configuration namespace. Also, I really appreciate the fact that you took the time to provide an example, I wish more did this. "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)." I used about 20 lines less in my example ;-) Seriously though, I can definitely see the advantages. Thanks for your reply. Jared Show quote "Jay B. Harlow [MVP - Outlook]" wrote: > | 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/library/system.configuration.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/codegallery.aspx?id=295a464a-6072-4e25-94e2-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.com> wrote in message > news:F3CC197E-993A-4888-A594-2DB45B7E0B9A@microsoft.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 |
|||||||||||||||||||||||