This week I had to create a relatively simple Windows console program that would be scheduled nightly to email a report to a client. Simple enough, right?
Well, I decided I to make the program a little more flexible so that I could easily add more reports later if needed and specify through command-line arguments which report would be run.
So, I decided I would try and make a custom configuration section in the app.config file that would look like this...
<Reports>
<add name="DailyProductionRpt" emailTo="customer@some.com" lastRun="12/15/2007"/>
<add name="NavyMonthlyProduction" emailTo="customer@some.com" lastRun="11/30/2007"/>
</Reports>
Sounds simple enough, huh? This looks just like the <connectionStrings> section so there should be a million examples on the web on how to do it, right? So, I started Googling for examples on how to implement a custom section handler and found a lot of great examples on how to create a custom section if you want to put the collection element inside another element such as like this...
<MyCustomSection>
<Reports>
<add .../>
<add .../>
</Reports>
</MyCustomSection>
But, I didn't want that extra element and didn't see any need to include it. So, after spending several days working on this between other tasks I finally got it working. I'm posting my solution here since there doesn't appear to be an example like this anywhere else on the web.
First you have to add this code to the <configSections> section of your app.config file...
<configSections>
<section name="Reports" type="MyCompany.DataDelivery.ReportsConfigSection, DataDelivery"/>
</configSections>
Where "ReportsConfigSection" is the name of the section handler class and "DataDelivery" is the name of the assembly where it exists.
Here's all the custom section handler code that had to be implemented...
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
namespace MyCompany.DataDelivery
{
/// <summary>
/// The Handler for the custom "Reports" configuration section.
/// </summary>
public sealed class ReportsConfigSection : ConfigurationSection
{
private static ReportsCollection _reports;
public ReportsConfigSection()
{
_reports = (ReportsCollection)base[""];
}
[ConfigurationProperty("", IsDefaultCollection = true, IsRequired = true)]
public ReportsCollection Reports
{
get
{
_reports = (ReportsCollection)base[""];
return _reports;
}
}
public ReportElement this[int idx]
{
get
{
return Reports[idx];
}
}
public ReportElement this[object idx]
{
get
{
return Reports[idx];
}
}
}
/// <summary>
/// This defines the Reports collection element.
/// </summary>
ConfigurationCollection(typeof(ReportElement))]
public class ReportsCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ReportElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ReportElement)element).Name;
}
public ReportElement this[int idx]
{
get
{
return (ReportElement)BaseGet(idx);
}
}
public ReportElement this[object idx]
{
get
{
return (ReportElement)BaseGet(idx);
}
}
}
/// <summary>
/// The class that holds onto each element returned by the configuration manager.
/// </summary>
public class ReportElement : ConfigurationElement
{
[ConfigurationProperty("name", DefaultValue = "", IsKey = true, IsRequired = true)]
public string Name
{
get
{
return ((string)(base["name"]));
}
set
{
base["name"] = value;
}
}
[ConfigurationProperty("emailTo", DefaultValue = "", IsKey = false, IsRequired = false)]
public string EmailTo
{
get
{
return ((string)(base["emailTo"]));
}
set
{
base["emailTo"] = value;
}
}
[ConfigurationProperty("lastRun", DefaultValue = "", IsKey = false, IsRequired = false)]
public DateTime LastRun
{
get
{
return ((DateTime)(base["lastRun"]));
}
set
{
base["lastRun"] = value;
}
}
}
}
I'm still not sure that I understand everything happening here, but it works and you should be able to easily adapt it to your needs if you need a custom top-level collection element in your configuration file.
0 comments:
Post a Comment