reading external configuration file

72.8k views Asked by At

I have a c# .Net console app that performs FTP operations. Currently, I specify the settings in a custom configuration section, e.g.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="ftpConfiguration" type="FileTransferHelper.FtpLibrary.FtpConfigurationSection, FileTransferHelper.FtpLibrary" />
  </configSections>

  <ftpConfiguration>
      <Environment name="QA">
        <sourceServer hostname="QA_hostname"
                      username="QA_username"
                      password="QA_password"
                      port="21"
                      remoteDirectory ="QA_remoteDirectory" />
        <targetServer downloadDirectory ="QA_downloadDirectory" />

      </Environment>
  </ftpConfiguration>

</configuration>

I would like to specify, in the command line, an external configuration file.

HOWEVER!!!...

I just realized that the above "FtpConfiguration" section doesn't truly belong in the application's app.config. My ultimate goal is that I will have many scheduled tasks that execute my console app like this:

FileTransferHelper.exe -c FtpApplication1.config
FileTransferHelper.exe -c FtpApplication2.config
...
FileTransferHelper.exe -c FtpApplication99.config

Consequently, I believe I've gone down the wrong path and what I really want is something to read in my custom xml document but continue to use System.Configuration to get the values... as opposed to reading an XmlDocument and serializing it to get nodes/elements/attributes. (Although, I'm not against the latter if someone can show me some simple code)

Pointers would be greatly appreciated. Thanks.

Update: The answer I accepted was a link to another StackOverflow question, repeated here with my code - below which was exactly what I was looking for -- using the OpenMappedExeConfiguration to open my external config file

ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = @"D:\Development\FileTransferHelper\Configuration\SampleInterface.config";

Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);

FtpConfigurationSection ftpConfig = (FtpConfigurationSection)config.GetSection("ftpConfiguration");
3

There are 3 answers

0
OnoSendai On BEST ANSWER

If you want to use System.Configuration to open your custom files, you may want to check on this post: Loading custom configuration files. Oliver nails it in a very straightforward way.

Since you want to read parameters passed to your application via the command line, you may want to pay a visit to this MSDN post: Command Line Parameters Tutorial.

If you'd rather use a custom approach, there's a few ways you can accomplish this. One possibility is to implement a loader class, and consume your custom configuration files.

For example, let's assume a simple config file that looks like this:

spec1.config

<?xml version="1.0" encoding="utf-8"?>
<Settings>
    <add key="hostname" value="QA_hostname" />
    <add key="username" value="QA_username" />
</Settings>

A very simple, hashtable-like (key-value pair) structure.

An implemented parser/reader would then look something like this:

        private Hashtable getSettings(string path)
        {
            Hashtable _ret = new Hashtable();
            if (File.Exists(path))
            {
                StreamReader reader = new StreamReader
                (
                    new FileStream(
                        path,
                        FileMode.Open,
                        FileAccess.Read,
                        FileShare.Read)
                );
                XmlDocument doc = new XmlDocument();
                string xmlIn = reader.ReadToEnd();
                reader.Close();
                doc.LoadXml(xmlIn);
                foreach (XmlNode child in doc.ChildNodes)
                    if (child.Name.Equals("Settings"))
                        foreach (XmlNode node in child.ChildNodes)
                            if (node.Name.Equals("add"))
                                _ret.Add
                                (
                                    node.Attributes["key"].Value,
                                    node.Attributes["value"].Value
                                );
            }
            return (_ret);
        }

Meanwhile, you'll still be able to use ConfigurationManager.AppSettings[] to read from the original app.config file.

2
Magus On

My preferred solution uses an XDocument. I haven't tested it, so there may be some slight issues, but this is to demonstrate my point.

public Dictionary<string, string> GetSettings(string path)
{

  var document = XDocument.Load(path);

  var root = document.Root;
  var results =
    root
      .Elements()
      .ToDictionary(element => element.Name.ToString(), element => element.Value);

  return results;

}

Will return a dictionary containing the element names and values from xml of the form:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <hostname>QA_hostname</hostname>
  <username>QA_username</username>
</root>

I find this solution nice because of it's overall brevity.

Again, I don't expect this to work exactly as is. Using XAttributes and XElements and such, you can definitely make this more like your original. It will be easy to filter.

0
Josh Bowden On

If you going down the custom path, I honestly would just use JSON to store the config and then deserialize to load it and serialize to write it. Json.NET allows you to do this very easily.

Your XML of:

<ftpConfiguration>
  <Environment name="QA">
    <sourceServer hostname="QA_hostname"
                  username="QA_username"
                  password="QA_password"
                  port="21"
                  remoteDirectory ="QA_remoteDirectory" />
    <targetServer downloadDirectory ="QA_downloadDirectory" />

  </Environment>
</ftpConfiguration>

Would look like this in JSON:

{
  "FtpConfiguration": {
    "Environment": {
      "Name": "QA",
      "SourceServer": {
        "HostName": "QA_hostname",
        "UserName": "QA_username",
        "Password": "QA_password",
        "Port": "21",
        "RemoteDirectory": "QA_remoteDirectory"
      },
      "TargetServer": {
        "DownloadDirectory": "QA_downloadDirectory"
      }
    }
  }
}

Your classes would look like:

class Config
{
    public FtpConfiguration FtpConfiguration { get; set; }
}

class FtpConfiguration
{
    public Environment Environment { get; set; }
}

class Environment
{
    public SourceServer SourceServer { get; set; }
    public TargetServer TargetServer { get; set; }
}

class SourceServer
{
    public string HostName { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public int Port { get; set; }
    public string RemoteDirectory { get; set; }
}

class TargetServer
{
    public string DownloadDirectory { get; set; }
}

You would save setting into an object like this:

var config = new Config()
{
    FtpConfiguration = new FtpConfiguration()
    {
        Environment = new Environment()
        {
            SourceServer = new SourceServer()
            {
                HostName = "localhost",
                UserName = "jaxrtech",
                Password = "stackoverflowiscool",
                Port = 9090,
                RemoteDirectory = "/data",
            },
            TargetServer = new TargetServer()
            {
                DownloadDirectory = "/downloads"
            }
        }
    }
};

You then could write to the file like this (or use a Stream if its a bigger file):

string json = JsonConvert.SerializeObject(config);
File.WriteAllText("config.json", json);

You then could read in the file like this (again you could use a Stream instead):

string json = File.ReadAllText("config.json");
Config config = JsonConvert.DeserializeObject<Config>(json);