• 0

[req] Problem reading app.config file using C#


Question

Hello, I need a little help

Actually, I want to create a windows based tool that will contain app.config file using Visual Studio 2005

The App.Config file will details regarding

connectionstring

schema

username/password

IT will also contain two table

OPC_GROUP

and

PARAMETERS

These tables are present in the Oracle database which we connect too

Now, I want to add some custom information within the App.config file

The custom information holds

Fields, Value;

This values changes frequently

My problem is how can i hold these fields and its correspoinding values in config file??????

and later able to read those information on runtime....... so as to populate the data in the oracle table

Any help would be deeply appreciated

Here is my tryout code

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>	
	<add key="SDEConnection" value="Connection to GISDatabase"/>
	<add key="SchemaName" value="DB"/>
	<add key="DBConnString" value="Server=gisdb;User ID=user;Password=pass"/>	
	<add key="GroupTable" value="OPC_GROUP"/>
	<add key="ParameterTable" value="PARAMETER"/>
  </appSettings>

  <GroupTable>
	<GroupRow>
	  <GroupColumn name="GroupID" columnName="GROUP_ID"  value="MUM_CB"/>
	  <GroupColumn name="Description" columnName="DESCRIPTION"  value="Mumbai CB Status"/>
	  <GroupColumn name="Parameter" columnName="PARAMETER_NAME"  value="CB'@<ACTUAL STATUS>"/>
	  <GroupColumn name="Percentage" columnName="PERCENTAGE_CHANGE"  value="10"/>
	  <GroupColumn name="Time" columnName="TIME_INTERVAL"  value="60000"/>
	</GroupRow>

	<GroupRow>
	  <GroupColumn name="GroupID" columnName="GROUP_ID"  value="MUM_CB_AT"/>
	  <GroupColumn name="Description" columnName="DESCRIPTION"  value="Mumbai CB Autotrip Status"/>
	  <GroupColumn name="Parameter" columnName="PARAMETER_NAME"  value="AUTOTRIP'@<ACTUAL STATUS>"/>
	  <GroupColumn name="Percentage" columnName="PERCENTAGE_CHANGE"  value="10"/>
	  <GroupColumn name="Time" columnName="TIME_INTERVAL"  value="60000"/>
	</GroupRow>

	<GroupRow>
	  <GroupColumn name="GroupID" columnName="GROUP_ID"  value="MUM_SW"/>
	  <GroupColumn name="Description" columnName="DESCRIPTION"  value="Mumbai Switch Status"/>
	  <GroupColumn name="Parameter" columnName="PARAMETER_NAME"  value="SW'@<ACTUAL STATUS>"/>
	  <GroupColumn name="Percentage" columnName="PERCENTAGE_CHANGE"  value="10"/>
	  <GroupColumn name="Time" columnName="TIME_INTERVAL"  value="60000"/>
	</GroupRow>

	<GroupRow>
	  <GroupColumn name="GroupID" columnName="GROUP_ID"  value=""/>
	  <GroupColumn name="Description" columnName="DESCRIPTION"  value=""/>
	  <GroupColumn name="Parameter" columnName="PARAMETER_NAME"  value=""/>
	  <GroupColumn name="Percentage" columnName="PERCENTAGE_CHANGE"  value=""/>
	  <GroupColumn name="Time" columnName="TIME_INTERVAL"  value=""/>
	</GroupRow>
  </GroupTable>

  <ParameterTable>
	<ParameterRow>
	  <ParameterColumn name="GISFeature" columnName="GIS_FEATURE_CLASS_NAME" value="CircuitBreaker"/>
	  <ParameterColumn name="ScadaTable" columnName="SCADA_TABLE_NAME" value="CB_SCADA_EVENTS"/>
	  <ParameterColumn name="ValueCol" columnName="VALUE_COLUMN_NAME" value="CBSTATUS_VALUE" />
	  <ParameterColumn name="TimestampCol" columnName="TIMESTAMP_COLUMN_NAME" value="CBSTATUS_TIME"/>
	  <ParameterColumn name="Parameter" columnName="PARAMETER_NAME" value="CB'@<ACTUAL STATUS>"/>
	  <ParameterColumn name="QualityCol" columnName="QUALITY_COLUMN_NAME" value="CBSTATUS_QUALITY"/>
	  <ParameterColumn name="City" columnName="CITY" value="MUM"/>
	  <ParameterColumn name="GISAttribute" columnName="GIS_ATTRIBUTE" value="CBSTATUS"/>
	  <ParameterColumn name="MinVal" columnName="MINVALUE" value=""/>
	  <ParameterColumn name="MaxVal" columnName="MAXVALUE" value=""/>
	  <ParameterColumn name="UpdatedCol" columnName="UPDATED_COLUMN_NAME" value="CBSTATUS_UPDATED"/>
	</ParameterRow>

	<ParameterRow>
	  <ParameterColumn name="GISFeature" columnName="GIS_FEATURE_CLASS_NAME" value="CircuitBreaker"/>
	  <ParameterColumn name="ScadaTable" columnName="SCADA_TABLE_NAME" value="CB_SCADA_EVENTS"/>
	  <ParameterColumn name="ValueCol" columnName="VALUE_COLUMN_NAME" value="CBAUTOTRIP_VALUE"/>
	  <ParameterColumn name="TimestampCol" columnName="TIMESTAMP_COLUMN_NAME" value="CBAUTOTRIP_TIME"/>
	  <ParameterColumn name="Parameter" columnName="PARAMETER_NAME" value="AUTOTRIP'@<ACTUAL STATUS>"/>
	  <ParameterColumn name="QualityCol" columnName="QUALITY_COLUMN_NAME" value="CBAUTOTRIP_QUALITY"/>
	  <ParameterColumn name="City" columnName="CITY" value="MUM"/>
	  <ParameterColumn name="GISAttribute" columnName="GIS_ATTRIBUTE" value="CBAUTOTRIP"/>
	  <ParameterColumn name="MinVal" columnName="MINVALUE" value=""/>
	  <ParameterColumn name="MaxVal" columnName="MAXVALUE" value=""/>
	  <ParameterColumn name="UpdatedCol" columnName="UPDATED_COLUMN_NAME" value="CBAUTOTRIP_UPDATED"/>
	</ParameterRow>

	<ParameterRow>
	  <ParameterColumn name="GISFeature" columnName="GIS_FEATURE_CLASS_NAME" value="Switch"/>
	  <ParameterColumn name="ScadaTable" columnName="SCADA_TABLE_NAME" value="SW_SCADA_EVENTS"/>
	  <ParameterColumn name="ValueCol" columnName="VALUE_COLUMN_NAME" value="SWSTATUS_VALUE" datatype="number" size="4"/>
	  <ParameterColumn name="TimestampCol" columnName="TIMESTAMP_COLUMN_NAME" value="SWSTATUS_TIME" datatype="date"/>
	  <ParameterColumn name="Parameter" columnName="PARAMETER_NAME" value="SW'@<ACTUAL STATUS>"/>
	  <ParameterColumn name="QualityCol" columnName="QUALITY_COLUMN_NAME" value="SWSTATUS_QUALITY" datatype="varchar2" size="255"/>
	  <ParameterColumn name="City" columnName="CITY" value="MUM"/>
	  <ParameterColumn name="GISAttribute" columnName="GIS_ATTRIBUTE" value="SWSTATUS"/>
	  <ParameterColumn name="MinVal" columnName="MINVALUE" value=""/>
	  <ParameterColumn name="MaxVal" columnName="MAXVALUE" value=""/>
	  <ParameterColumn name="UpdatedCol" columnName="UPDATED_COLUMN_NAME" value="SWSTATUS_UPDATED" datatype="varchar2" size="1"/>
	</ParameterRow>

	<ParameterRow>
	  <ParameterColumn name="GISFeature" columnName="GIS_FEATURE_CLASS_NAME" value=""/>
	  <ParameterColumn name="ScadaTable" columnName="SCADA_TABLE_NAME" value=""/>
	  <ParameterColumn name="ValueCol" columnName="VALUE_COLUMN_NAME" value=""/>
	  <ParameterColumn name="TimestampCol" columnName="TIMESTAMP_COLUMN_NAME" value=""/>
	  <ParameterColumn name="Parameter" columnName="PARAMETER_NAME" value=""/>
	  <ParameterColumn name="QualityCol" columnName="QUALITY_COLUMN_NAME" value=""/>
	  <ParameterColumn name="City" columnName="CITY" value=""/>
	  <ParameterColumn name="GISAttribute" columnName="GIS_ATTRIBUTE" value=""/>
	  <ParameterColumn name="MinVal" columnName="MINVALUE" value=""/>
	  <ParameterColumn name="MaxVal" columnName="MAXVALUE" value=""/>
	  <ParameterColumn name="UpdatedCol" columnName="UPDATED_COLUMN_NAME" value=""/>
	</ParameterRow>
  </ParameterTable>

</configuration>

Link to comment
Share on other sites

6 answers to this question

Recommended Posts

  • 0

Hi,

.NET 2.0 brought about a replumbed configuration mechanism. What you could do is create a few configuration element and collection classes to handle this custom information:

Firstly, we could use a column class:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a column.
	/// </summary>
	public class ColumnConfigurationElement : ConfigurationElement
	{
		#region Constants
		private const string CFG_NAME = "name";
		private const string CFG_COLUMNNAME = "columnName";
		private const string CFG_VALUE = "value";
		private const string CFG_DATATYPE = "datatype";
		private const string CFG_SIZE = "size";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_NAME, IsKey = true, IsRequired = true)]
		public string Name { get { return (string)this[CFG_NAME]; } set { this[CFG_NAME] = value; } }

		[ConfigurationProperty(CFG_COLUMNNAME, IsKey = false, IsRequired = true)]
		public string ColumnName { get { return (string)this[CFG_COLUMNNAME]; } set { this[CFG_COLUMNNAME] = value; } }

		[ConfigurationProperty(CFG_VALUE, IsKey = false, IsRequired = false, DefaultValue = (string)null)]
		public string Value { get { return (string)this[CFG_VALUE]; } set { this[CFG_VALUE] = value; } }

		[ConfigurationProperty(CFG_DATATYPE, IsKey = false, IsRequired = false, DefaultValue = (string)null)]
		public string DataType { get { return (string)this[CFG_DATATYPE]; } set { this[CFG_DATATYPE] = value; } }

		[ConfigurationProperty(CFG_SIZE, IsKey = false, IsRequired = false, DefaultValue = 0)]
		public int Size { get { return (int)this[CFG_SIZE]; } set { this[CFG_SIZE] = value; } }
		#endregion
	}
}

This ConfigurationPropertyAttribute attribute is used by the configuration section to map xml elements to their property equivalents. For this ColumnConfigurationElement, we are specifying properties for name, columnName, value, datatype and size. These get mapped over into the properties Name, ColumnName, Value, DataType and Size. The ConfigurationPropertyAttribute applied to each property allows us to set properties like:

  • IsKey - is this the identifying property of the element.
  • IsRequired - is this a required property.
  • DefaultValue - the value applied to the property where no value is specified

We know that columns belong in a collection, so we create a collection to handle this:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a collection of columns.
	/// </summary>
	[ConfigurationCollection(typeof(ColumnConfigurationElement), AddItemName = "Column")]
	public class ColumnConfigurationElementCollection : ConfigurationElementCollection
	{
		#region Methods
		protected override ConfigurationElement CreateNewElement()
		{
			return new ColumnConfigurationElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			/*
			 * The column's name property (read: not ColumnName) is used to identify this element.
			 */
			return ((ColumnConfigurationElement)element).Name;
		}
		#endregion
	}
}

The beauty of the .NET Configuration system, is that all the underlying mapping is done for you. The ConfigurationCollectionAttribute attribute I have applied here tells the configuration system that it is a collection of type ColumnConfigurationElement, and the add command (used in the config file) should be called "Column", it would default to "add" if this were no present. The ConfigurationElementCollection base class requires us to implement some abstract methods:

  • CreateNewElement - create a new instance of the containing type of the collection.
  • GetElementKey - gets a value that uniquely identifies this instance of the containing type within the collection.

Going up the tree, a row contains columns, so we need to create a configuration element for that:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents an individual row.
	/// </summary>
	public class RowConfigurationElement : ConfigurationElement
	{
		#region Constants
		private const string CFG_COLUMNS = "Columns";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_COLUMNS, IsRequired = true, IsDefaultCollection = true)]
		public ColumnConfigurationElementCollection Columns
		{
			get { return (ColumnConfigurationElementCollection)this[CFG_COLUMNS]; }
			set { this[CFG_COLUMNS] = value; }
		}
		#endregion
	}
}

We only specify one property here, which is the property that is the collection of columns. We also need a containing collection of rows:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a collection of rows.
	/// </summary>
	[ConfigurationCollection(typeof(RowConfigurationElement), AddItemName = "Row")]
	public class RowConfigurationElementCollection : ConfigurationElementCollection
	{
		#region Methods
		protected override ConfigurationElement CreateNewElement()
		{
			return new RowConfigurationElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			/*
			 * A row doesn't actually have an identifying property, so we will just return a random Guid.
			 */
			return Guid.NewGuid();
		}
		#endregion
	}
}

I've cheated here, because a row doesn't have any identifying property, we'll just generate a random one, so I've used a guid.

Rows are contained in a table, but as this is the root of the tree, we'll use a ConfigurationSection instead of a ConfigurationElement as its base:

namespace ConsoleApplication1
{
	public class TableConfigurationSection : ConfigurationSection
	{
		#region Constants
		private const string CFG_ROWS = "Rows";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_ROWS, IsRequired = true, IsDefaultCollection = true)]
		public RowConfigurationElementCollection Rows
		{
			get { return (RowConfigurationElementCollection)this[CFG_ROWS]; }
			set { this[CFG_ROWS] = value; }
		}
		#endregion

		#region Methods
		public static TableConfigurationSection GetConfiguration(string name)
		{
			return ConfigurationManager.GetSection(name) as TableConfigurationSection;
		}
		#endregion
	}
}

I added the static method GetConfiguration to allow us to grab the configuration section from the config file. You can then modify the configuration file to support this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
	<section name="GroupTable" type="ConsoleApplication1.TableConfigurationSection, ConsoleApplication1" />
	<section name="ParameterTable" type="ConsoleApplication1.TableConfigurationSection, ConsoleApplication1" />
  </configSections>

  <GroupTable>
	<Rows>
	  <Row>
		<Columns>
		  <Column name="Test" columnName ="TEST" value="test value" />
		  <Column name="Test1" columnName ="TEST1" value="test value1" />
		  <Column name="Test2" columnName ="TEST2" value="test value2" />  
		</Columns>
	  </Row>
	</Rows>
  </GroupTable>
</configuration>

You will probably want to read up on custom .NET configuration files for more information.

If you want a cleaner xml config, you could override the OnDeserializeUnknownElement which will pass you the element name and an instance of the underlying XmlReader used to read the config. You would need to perform your own element to property mappings though.

I've included my sample project for you.

ConsoleApplication1.zip

Link to comment
Share on other sites

  • 0

Thnk you so much..... would look into this because even in MSDN i was not able find the explanation

also.... srrry for more trouble sample project wont open for me....

I m using vista sp1 + vs2005 neither in xp sp3 + vs2005 (updated)

Link to comment
Share on other sites

  • 0
Hi,

.NET 2.0 brought about a replumbed configuration mechanism. What you could do is create a few configuration element and collection classes to handle this custom information:

Firstly, we could use a column class:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a column.
	/// </summary>
	public class ColumnConfigurationElement : ConfigurationElement
	{
		#region Constants
		private const string CFG_NAME = "name";
		private const string CFG_COLUMNNAME = "columnName";
		private const string CFG_VALUE = "value";
		private const string CFG_DATATYPE = "datatype";
		private const string CFG_SIZE = "size";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_NAME, IsKey = true, IsRequired = true)]
		public string Name { get { return (string)this[CFG_NAME]; } set { this[CFG_NAME] = value; } }

		[ConfigurationProperty(CFG_COLUMNNAME, IsKey = false, IsRequired = true)]
		public string ColumnName { get { return (string)this[CFG_COLUMNNAME]; } set { this[CFG_COLUMNNAME] = value; } }

		[ConfigurationProperty(CFG_VALUE, IsKey = false, IsRequired = false, DefaultValue = (string)null)]
		public string Value { get { return (string)this[CFG_VALUE]; } set { this[CFG_VALUE] = value; } }

		[ConfigurationProperty(CFG_DATATYPE, IsKey = false, IsRequired = false, DefaultValue = (string)null)]
		public string DataType { get { return (string)this[CFG_DATATYPE]; } set { this[CFG_DATATYPE] = value; } }

		[ConfigurationProperty(CFG_SIZE, IsKey = false, IsRequired = false, DefaultValue = 0)]
		public int Size { get { return (int)this[CFG_SIZE]; } set { this[CFG_SIZE] = value; } }
		#endregion
	}
}

This ConfigurationPropertyAttribute attribute is used by the configuration section to map xml elements to their property equivalents. For this ColumnConfigurationElement, we are specifying properties for name, columnName, value, datatype and size. These get mapped over into the properties Name, ColumnName, Value, DataType and Size. The ConfigurationPropertyAttribute applied to each property allows us to set properties like:

  • IsKey - is this the identifying property of the element.
  • IsRequired - is this a required property.
  • DefaultValue - the value applied to the property where no value is specified

We know that columns belong in a collection, so we create a collection to handle this:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a collection of columns.
	/// </summary>
	[ConfigurationCollection(typeof(ColumnConfigurationElement), AddItemName = "Column")]
	public class ColumnConfigurationElementCollection : ConfigurationElementCollection
	{
		#region Methods
		protected override ConfigurationElement CreateNewElement()
		{
			return new ColumnConfigurationElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			/*
			 * The column's name property (read: not ColumnName) is used to identify this element.
			 */
			return ((ColumnConfigurationElement)element).Name;
		}
		#endregion
	}
}

The beauty of the .NET Configuration system, is that all the underlying mapping is done for you. The ConfigurationCollectionAttribute attribute I have applied here tells the configuration system that it is a collection of type ColumnConfigurationElement, and the add command (used in the config file) should be called "Column", it would default to "add" if this were no present. The ConfigurationElementCollection base class requires us to implement some abstract methods:

  • CreateNewElement - create a new instance of the containing type of the collection.
  • GetElementKey - gets a value that uniquely identifies this instance of the containing type within the collection.

Going up the tree, a row contains columns, so we need to create a configuration element for that:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents an individual row.
	/// </summary>
	public class RowConfigurationElement : ConfigurationElement
	{
		#region Constants
		private const string CFG_COLUMNS = "Columns";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_COLUMNS, IsRequired = true, IsDefaultCollection = true)]
		public ColumnConfigurationElementCollection Columns
		{
			get { return (ColumnConfigurationElementCollection)this[CFG_COLUMNS]; }
			set { this[CFG_COLUMNS] = value; }
		}
		#endregion
	}
}

We only specify one property here, which is the property that is the collection of columns. We also need a containing collection of rows:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a collection of rows.
	/// </summary>
	[ConfigurationCollection(typeof(RowConfigurationElement), AddItemName = "Row")]
	public class RowConfigurationElementCollection : ConfigurationElementCollection
	{
		#region Methods
		protected override ConfigurationElement CreateNewElement()
		{
			return new RowConfigurationElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			/*
			 * A row doesn't actually have an identifying property, so we will just return a random Guid.
			 */
			return Guid.NewGuid();
		}
		#endregion
	}
}

I've cheated here, because a row doesn't have any identifying property, we'll just generate a random one, so I've used a guid.

Rows are contained in a table, but as this is the root of the tree, we'll use a ConfigurationSection instead of a ConfigurationElement as its base:

namespace ConsoleApplication1
{
	public class TableConfigurationSection : ConfigurationSection
	{
		#region Constants
		private const string CFG_ROWS = "Rows";
		#endregion

		#region Properties
		[ConfigurationProperty(CFG_ROWS, IsRequired = true, IsDefaultCollection = true)]
		public RowConfigurationElementCollection Rows
		{
			get { return (RowConfigurationElementCollection)this[CFG_ROWS]; }
			set { this[CFG_ROWS] = value; }
		}
		#endregion

		#region Methods
		public static TableConfigurationSection GetConfiguration(string name)
		{
			return ConfigurationManager.GetSection(name) as TableConfigurationSection;
		}
		#endregion
	}
}

I added the static method GetConfiguration to allow us to grab the configuration section from the config file. You can then modify the configuration file to support this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
	<section name="GroupTable" type="ConsoleApplication1.TableConfigurationSection, ConsoleApplication1" />
	<section name="ParameterTable" type="ConsoleApplication1.TableConfigurationSection, ConsoleApplication1" />
  </configSections>

  <GroupTable>
	<Rows>
	  <Row>
		<Columns>
		  <Column name="Test" columnName ="TEST" value="test value" />
		  <Column name="Test1" columnName ="TEST1" value="test value1" />
		  <Column name="Test2" columnName ="TEST2" value="test value2" />  
		</Columns>
	  </Row>
	</Rows>
  </GroupTable>
</configuration>

You will probably want to read up on custom .NET configuration files for more information.

If you want a cleaner xml config, you could override the OnDeserializeUnknownElement which will pass you the element name and an instance of the underlying XmlReader used to read the config. You would need to perform your own element to property mappings though.

I've included my sample project for you.

For some reason these program wont run for me....? any ideas, i m using vs2005

Link to comment
Share on other sites

  • 0

Sorry for the late reply, but I think I see what you've done:

	class RowConfigurationElementCollection
	{/// <summary>
		/// Represents a collection of rows.
		/// </summary>
		[ConfigurationCollection(typeof(RowConfigurationElement), AddItemName = "Row")]
		public class RowConfigurationElementCollection : ConfigurationElementCollection
		{
			#region Methods
			protected override ConfigurationElement CreateNewElement()
			{
				return new RowConfigurationElement();
			}

			protected override object GetElementKey(ConfigurationElement element)
			{
				/*
				 * A row doesn't actually have an identifying property, so we will just return a random Guid.
				 */
				return Guid.NewGuid();
			}
			#endregion
		}
	}

Where you have copied and pasted the code into your VS2005 format project, you have actually pasted the class as an inner class to a class of the same name (say that 10 times while drunk ;)). You need to remove the outer class declaration, so the inner classes are actual child types of the namespace itself:

namespace ConsoleApplication1
{
	/// <summary>
	/// Represents a collection of rows.
	/// </summary>
	[ConfigurationCollection(typeof(RowConfigurationElement), AddItemName = "Row")]
	public class RowConfigurationElementCollection : ConfigurationElementCollection
	{
		#region Methods
		protected override ConfigurationElement CreateNewElement()
		{
			return new RowConfigurationElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			/*
			 * A row doesn't actually have an identifying property, so we will just return a random Guid.
			 */
			return Guid.NewGuid();
		}
		#endregion
	}
}

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.