0

I am building a c# Windows application to maintain a WEB application - special to create and update values in a web.config file. I have tried many suggestions here, but all of them are simply describing to read and write config files from inside the application. Part of the web.config:

- 
 <configuration>
    <system.web>
        <customErrors mode="Off"/>

        <compilation debug="true" targetFramework="4.0" />
        <globalization culture="" enableClientBasedCulture="true" uiCulture="" />

        <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
    </system.web>
    <appSettings>
        <add key="DocPath" value="~/serverdocs/docs" />

        <add key="TextForLabel1" value="This is some text"/>
..

I tried to read the file - special all keys from appSettings -like

    var xmlDoc = new XmlDocument();
        xmlDoc.Load(FName); // name and path from web.config file

And then to read all nodes like :

            foreach (XmlNodeList xmlnodes in xmlDoc.SelectNodes("configuration/appSettings"))
            {
                foreach ( XmlNode node in xmlnodes)
                {
                string MyKey = node.Name;
                string MyVal = node.Value;
                }

            }  

But there are always errors like The object of type "System.Xml.XmlDeclaration" cannot be converted to Type "System.Xml.XmlNodeList". Or the items are simply not found depending on how I write the select value. I have tried '//configuration' and 'configuration/appSettings' and others.

Sometimes I am probably blind reading my own code and detecting the error - sorry - any suggestion is welcome.

Wilhelm
  • 196
  • 10
  • Please provide a code and config sample that did produce the aforementioned exception. Config files are valid XML files, and thus can be read and manipulated by loading them into XmlDocument, appliying changes and writing them back to disk. – Alexander Gräf Jan 30 '19 at 23:04
  • The error in my code is in the first line of my code show above. False: is "foreach (XmlNodeList xmlnodes in xmlDoc.SelectNodes("configuration/appSettings"))" , true: " foreach (XmlElement xmlElement in xmlDoc.SelectNodes("configuration/appSettings")) " – Wilhelm Jan 31 '19 at 18:28

3 Answers3

1

Although the configuration files are XML they can be difficult to enumerate and locate in an XmlDocument.

Instead, you can actually use a WebConfigurationManager which is specifically designed to read and write web.config files:

Configuration cfg = WebConfigurationManager.OpenWebConfiguration("YOUR_WEB_CONFIG");

Once you have the data in the Configuration object, you can enumerate the sections and values of the configuration file.

You will need to add a reference to both System.Configuration and System.Web in order to utilise the above.

EDIT

Following Willhelm's comment that he was unable to open the web.config from the physical path of the file, the following snippet (stolen directly from this answer) can be used to load the file:

public static Configuration OpenConfigFile(string configPath)
{
    var configFile = new FileInfo(configPath);
    var vdm = new VirtualDirectoryMapping(configFile.DirectoryName, true, configFile.Name);
    var wcfm = new WebConfigurationFileMap();
    wcfm.VirtualDirectories.Add("/", vdm);
    return WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
}

To use:

Configuration cfg = OpenConfigFile(@"YOUR_WEB_CONFIG");
Martin
  • 16,093
  • 1
  • 29
  • 48
  • I tried that as well, but when using the path to the web.config like d:\apps\1\web.config it was rejected with message, that a path like that is not valid like relative path is not allowed or absolute path is not allowed. I tried even to place my windows app in the same folder as web.config is located without success using "~/web.config" as value for ."YOUR_WEB_CONFIG"; – Wilhelm Jan 30 '19 at 22:56
  • While I agree that opening a config file with the ConfigurationManager or WebConfigurationManager is good practice, can you elaborate on why a valid XML would not load into an XmlDocument? I'm pretty sure a .config file has to at least adhere to XML syntax, and that means that it must be loadable by XmlDocument. – Alexander Gräf Jan 30 '19 at 23:02
  • @AlexanderGräf I meant that it isn't as easy to specifically identify the sections and values from an XmlDocument as it is to use the built-in configuration managers. Apologies for the poor wording, I've rephrased the answer. – Martin Jan 30 '19 at 23:12
  • Actually, the ConfigManager has it's advantages when it comes to inheritance. Pinpointing and modifying certain nodes on the other hand works much better with XPath, because the ConfigManager requires you to manually iterate elements. And to my knowledge, it only works correctly when all configSection assemblies can be loaded. Otherwise the act of opening the file will already throw exceptions. – Alexander Gräf Jan 30 '19 at 23:17
  • @Wilhelm I have modified my answer to include addition information to allow you to load the `web.config` from a physical path. – Martin Jan 30 '19 at 23:21
  • Well the IIS is quite sensitive for a correct web.config. One error here and the page will not start. However - its start and so I assume, that the web.config is formatted as it should be. Please give me some time to follow the last suggestion added by Martin to trick the webconfigurationmanager open a special file. I will write my experiance about that! Thanks so far!!! – Wilhelm Jan 30 '19 at 23:27
0

to read it:

   var xmlDoc = new XmlDocument();
    xmlDoc.Load(FName);
    DataTable DTConfig = new DataTable();
    DTConfig.Columns.Add("Key", typeof(string));
    DTConfig.Columns.Add("Value", typeof(string));
    DTConfig.Columns.Add("OldValue", typeof(string));
    try
    {
        foreach (XmlElement xmlElement in xmlDoc.SelectNodes("configuration/appSettings"))
        {
            foreach (XmlNode node in xmlElement)
            {
                if (node.Attributes != null && node.Attributes.Count > 1)
                {
                    string MyKey = "";
                    string MyVal = "";
                    if (node.Attributes[0].Value != null)
                    {
                        MyKey = node.Attributes[0].Value;
                    }
                    if (node.Attributes[1].Value != null)
                    {
                        MyVal = node.Attributes[1].Value;
                    }
                    DataRow DR = DTConfig.NewRow();
                    DR["Key"] = MyKey;
                    DR["Value"] = MyVal;
                    DR["OldValue"] = MyVal;
                    if (MyKey != "")
                    {
                        DTConfig.Rows.Add(DR);
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.Message);
    }
    TxtFname.Text = FName;
    DGConfig.DataSource = DTConfig;
    return true;

The datatable is bound to a datagridview - thats all

Wilhelm
  • 196
  • 10
-1

I had a similar problem and went down the same road you did, trying to walk the XML tree to find the node I needed to change. It was a key/value pair that defined a the URI for a web service connection. The default URI pointed to localhost, e.g.:

    <add key="ProcessorService" value="https://localhost/Processor/ProcessorService.asmx" />

...and I needed to point it to the server upon which the service actually ran, which depends on which box it runs on (Dev, QA, UAT, Prod, etc.)

I pulled my hair out trying to walk that silly tree, and there were eight different web configs that needed similar modifications for each server upon which the sites were installed. Each web.config was structured differently, of course.

I solved it with a regex in the PowerShell installation script for the sites, here's a snippet:

(Get-Content $webConfig -Encoding UTF8) | `
    ForEach-Object {$_ -replace 'localhost', "$dnsName" } | `
    Set-Content $webConfig

I ran the above inside of a loop that went through each web.config file in each directory, made the modification, then saved the file with the Set-Content cmdlet.

I can understand if you have to do this in C#, you could always just open the file as a stream, have a few Regex objects set up, and perform essentially the same operation.

Me? Personally, I hate walking XML unless I'm forced to. You can most certainly do it that way, but if someone comes along and rearranges your web.config file, they'll break your nicely crafted XML walker and you'll be starting all over. This way, as long as what you're changing doesn't change (much) it should work just fine.

ggariepy
  • 1,027
  • 8
  • 8
  • Using Regex or other string manipulation tactics in XML files is always bad practice. – Alexander Gräf Jan 30 '19 at 23:03
  • That looks like a php code or something similar and unfortunately it will not solve my issue regarding maintain an external web.config file – Wilhelm Jan 30 '19 at 23:31
  • "Always bad practice?" I measure things by results, not abstract ideas on "practice.". It works, it saved my employer a lot of time and money, and that's really all that matters. – ggariepy Feb 01 '19 at 04:58
  • "It looks like PHP code" -- no, it's Powershell, you might have heard of it, the .Net scripting language that can be extended with .Net DLLs? Most of the deployment scripts I've seen use it in some fashion. – ggariepy Feb 01 '19 at 05:01
  • https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices?WT.mc_id=email – ggariepy Feb 01 '19 at 05:15