How to Read Custom XML from the app.config? - c#

I want to read the custom XML section from the app.config of a C# windows service.
How do I go about it?
The XML is below:
<Books>
<Book name="name1" title="title1"/>
<Book name="name2" title="title2"/>
</Books>

In a project I developed I use something similar for configuration that I found. I believe the article was called the last configuration section handler I'll ever need (I can't find a working link, maybe someone can link it for me).
This method takes what you want to do one step further, and actually de-serializes the object into memory. I'm just copying code from my project, but it should be fairly simple to take a step backwards if all you want is the XML.
First, you need to define a class that handles your configuration settings.
using System;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
namespace Ariel.config
{
class XmlSerializerSectionHandler : IConfigurationSectionHandler
{
#region IConfigurationSectionHandler Members
public object Create(object parent, object configContext, XmlNode section)
{
XPathNavigator nav = section.CreateNavigator();
string typename = (string)nav.Evaluate("string(#type)");
Type t = Type.GetType(typename);
XmlSerializer ser = new XmlSerializer(t);
return ser.Deserialize(new XmlNodeReader(section));
}
#endregion
}
}
Now, say you want to load a section of configuration... super easy, cast to the type of object you're expecting to XML Serialize to, and pass the section you're looking for (in this case SearchSettings.
try
{
config = (Eagle.Search.SearchSettings)ConfigurationSettings.GetConfig("SearchSettings");
}
catch (System.Configuration.ConfigurationException ex)
{
syslog.FatalException("Loading search configuration failed, you likely have an error", ex);
return;
}
Now, all you need is your App.config file. I chose to split mine into separate files (1 file per section) just to make managing the config a little easier. You define a section, give it a name, and define the type (whatever you called the class listed above) and the assembly's name.
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="SearchSettings" type="Ariel.config.XmlSerializerSectionHandler, Ariel"/>
</configSections>
<SearchSettings configSource="Config\Search.config" />
</configuration>
Now, all that's left is the config file to be de-serialized. What's important here is that the block matches your section name, and your type is whatever object it should de-serialize to, and the Assembly name.
<?xml version="1.0" encoding="utf-8" ?>
<SearchSettings type="Eagle.Search.SearchSettings, Eagle">
<NumThreads>4</NumThreads>
</SearchSettings>
If you just want the pure raw XML, all you should need to do is modify the Object that handles the section to return the XML or do whatever you need to do.

What you want to do is read up on Custom Configuration Sections.

Since IConfigurationSectionHandler is deprecated I thought it's worth mentioning that you can still implement a pure serialized section just by overriding ConfigurationSection.DeserializeSection and not calling the base implementation.
Here is a very basic example that I reuse a lot. A simple configuration section that loads an object graph from inline XAML. (Naturally you can implement with XmlSerializer instead)
using System.Configuration;
using System.Xaml;
using System.Xml;
...
public class XamlConfigurationSection<T> : ConfigurationSection
{
public static XamlConfigurationSection<T> Get(string sectionName)
{
return (XamlConfigurationSection<T>)ConfigurationManager.GetSection(sectionName);
}
public T Content { get; set; }
protected override void DeserializeSection(XmlReader xmlReader)
{
xmlReader.Read();
using (var xamlReader = new XamlXmlReader(xmlReader))
Content = (T)XamlServices.Load(xamlReader);
}
}

I use custom xml in my config.app. file and create a app.XSD from it.
The XSD file includes the schema of the config.app file.
Then XSD file can be translated to a vb class or a C# class file using 'xsd.exe'.
Now all you have to do is deserialize the configfile to the class.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="CustomConfig" type="Object" />
</configSections>
<CustomConfig>
<ActiveEnvironment>QAS</ActiveEnvironment>
<Environments>
<Environment name ="PRD" log="Y">
</Environment>
<Environment name ="QAS" log="N">
</Environment>
<Environment name ="DEV" log="Y">
</Environment>
</Environments>
</CustomConfig>
</configuration>

Related

Custom section in web.config and getting all items

I'm trying to create a section in web config where I can have a list of names.
So to do that I have done the following:
public class MyCustomSection : ConfigurationSection
{
public string Name
{
get
{
return (string) this["name"];
}
set
{
this["name"] = value;
}
}
}
In the web.config I have added the following:
<configSections>
<sectionGroup name="myCustomSectionGroup">
<section name="myCustomSection" type="MySite.MyCustomSection"/>
</sectionGroup>
</configSections>
<myCustomSectionGroup>
<myCustomSection name="MyFirstName"></myCustomSection>
<myCustomSection name="MySecondName"></myCustomSection>
</myCustomSectionGroup>
According to the MS guide, this is how its done: https://msdn.microsoft.com/en-us/library/2tw134k3.aspx
The next part I need to do is grab (in a IEnumerable format, list, array, anything really) myCustomSection. I need to know all the entries. However, I can't seem to find how that is done anywhere. Looking at the ConfigurationManager.GetSection, it returns an Object, not a list or ConfigurationSectionGroup or ConfigurationSection.
How do I grab the values from my custom section and loop over them, lets say just output them in console, their names.
The full answer to my question can be found in the following SO Question:
Correct implementation of a custom config section with nested collections?
The problem is that I can't create multiple sections, but I can create multiple elements of that section via collection. With that I have to configure the classes to support my layout. Please see the SO Answer posted above for full example.

Keep more of the url for requests SwaggerWcf

My WCF service is locally hosted like this:
localhost/service.svc
On the server it's hosted like this:
servername/extra/service.svc
When making a request SwaggerWcf uses the base path and adds the configered elements:
localhost/user/parameter
But it should be:
localhost/extra/user/parameter
So I need WCF to not just use the hostname, but also a little more of the url.
I could add it to the SwaggerWcf configuration, which is user and could be extra/user. But then it won't work locally anymore.
I've tried adding a Config class that could read configuration attributes statically:
using System.Configuration;
namespace Project
{
public static class Config
{
public static readonly string SwaggerWcfRequestPath = ConfigurationManager.AppSettings["SwaggerWcf.RequestPath"] ?? "/user";
}
}
But when I try to use this:
[SwaggerWcf(Config.SwaggerWcfRequestPath)]
public class Service: IService
It still produces the same error:
Error 46 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
How can this be done dynamically? Then it can be different for different environments.
Currently there is no way to do it
But I'll take a look into this later today
Probably the best way is allowing to override it using Web.config
<configSections>
<section name="swaggerwcf" type="SwaggerWcf.Configuration.SwaggerWcfSection, SwaggerWcf"/>
</configSections>
<swaggerwcf>
<settings>
<setting name="Host" value="www.msampleservice.com"/>
<setting name="BasePath" value="/myserviceapi"/>
</settings>
</swaggerwcf>

How to get all sections of a specific type

Let's say I have the following in my config:
<configSections>
<section name="interestingThings" type="Test.InterestingThingsSection, Test" />
<section name="moreInterestingThings" type="Test.InterestingThingsSection, Test" />
</configSections>
<interestingThings>
<add name="Thing1" value="Seuss" />
</interestingThings>
<moreInterestingThings>
<add name="Thing2" value="Seuss" />
</moreInterestingThings>
If I want to get either section, I can get them by name pretty easily:
InterestingThingsSection interesting = (InterestingThingsSection)ConfigurationManager.GetSection("interestingThings");
InterestingThingsSection more = (InterestingThingsSection)ConfigurationManager.GetSection("moreInterestingThings");
However, this relies on my code knowing how the section is named in the config - and it could be named anything. What I'd prefer is the ability to pull all sections of type InterestingThingsSection from the config, regardless of name. How can I go about this in a flexible way (so, supports both app configs and web configs)?
EDIT: If you have the Configuration already, getting the actual sections isn't too difficult:
public static IEnumerable<T> SectionsOfType<T>(this Configuration configuration)
where T : ConfigurationSection
{
return configuration.Sections.OfType<T>().Union(
configuration.SectionGroups.SectionsOfType<T>());
}
public static IEnumerable<T> SectionsOfType<T>(this ConfigurationSectionGroupCollection collection)
where T : ConfigurationSection
{
var sections = new List<T>();
foreach (ConfigurationSectionGroup group in collection)
{
sections.AddRange(group.Sections.OfType<T>());
sections.AddRange(group.SectionGroups.SectionsOfType<T>());
}
return sections;
}
However, how do I get the Configuration instance in a generally-applicable way? Or, how do I know if I should use ConfigurationManager or WebConfigurationManager?
So far, this appears to be the best way:
var config = HostingEnvironment.IsHosted
? WebConfigurationManager.OpenWebConfiguration(null) // Web app.
: ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); // Desktop app.
Try to use ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) method. It opens the configuration file for the current application as a Configuration object.
MSDN documentation: https://msdn.microsoft.com/en-us/library/ms134265%28v=vs.110%29.aspx
Maybe not the best way to do it but you can read your configuration file as a normal xml and then parse the sections you want. For example if it were a web application:
XmlDocument myConfig= new XmlDocument();
myConfig.Load(Server.MapPath("~/Web.config"));
XmlNode xnodes = myConfig.SelectSingleNode("/configSections");
Now you can see the nodes that you care about discovering the names at runtime and then access the specific node of your configuration file.
Another solution is:
Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
If this returns "web.config", it is probably a web application.
However, HostingEnvironment.IsHosted is intended to indicate whether an appdomain is configured to run under ASP.NET so it is not sure that yours is a web application.

How to get AppDomainSetup.SetConfigurationBytes() working?

In my application I'm creating sandbox domain and I need dynamically define binding redirect rules. However I cannot pass neither ConfigurationFile setting nor raw xml data using SetConfigurationBytes method.
I created a sample to check if configuration data is applied to child domain:
var configData =
#"<?xml version='1.0' encoding='utf-8' ?>
<configuration>
<appSettings>
<add key='MyData' value='CUSTOM'/>
</appSettings>
</configuration>";
var setup = new AppDomainSetup {
ApplicationBase = runtimeAssembliesPath // ,ConfigurationFile = configFilePath
};
setup.SetConfigurationBytes(Encoding.UTF8.GetBytes(configData));
var sandbox = AppDomain.CreateDomain("Sandbox", evidence, setup);
var verifier = (Sample)_sandbox.CreateInstanceAndUnwrap(thisAssemblyName, typeof(Sample).FullName);
verifier.DebugPrint();
where Verifier is plain class with one method:
public class Sample: MarshalByRefObject
{
public void DebugPrint()
{
Debug.WriteLine("=====================================");
Debug.Print("Reading config: =====|{0}|=====", ConfigurationManager.AppSettings["MyData"]);
Debug.WriteLine("=====================================");
}
}
Update
The first issue with code above is that it must be inherited from MarshalByRefObject to marshal DebugPrint call to child appdomain instead of making call on copy created in current domain (thanks sthotakura).
As a result ConfigurationFile setting begins to work.However still no luck with SetConfigurationBytes. Anyway the goal was to define bindingRules which DOES work if passed via SetConfigurationBytes.
To access an object across AppDomain boundary, the class must be derived from MarshalByRefObject.
From MSDN Page:
Types must inherit from MarshalByRefObject when the type is used
across application domain boundaries, and the state of the object must
not be copied because the members of the object are not usable outside
the application domain where they were created.
That means, your Sample Class should derive from MarshalByRefObject:
[Serializable]
public class Sample : MarshalByRefObject
{
public void DebugPrint()
{
Console.WriteLine("=====================================");
Console.WriteLine(ConfigurationManager.AppSettings.Count);
Console.WriteLine("Reading config: =====|{0}|=====", ConfigurationManager.AppSettings["MyData"]);
Console.WriteLine("=====================================");
}
}
I have tried in a sample app, but with a Config XML instead of calling SetConfigurationData and it works, Below is my Sample code for your reference:
var setup = new AppDomainSetup();
setup.ConfigurationFile = "SandBoxConfig.xml";
var sandbox = AppDomain.CreateDomain("Sandbox", null, setup);
var verifier = (Sample)sandbox.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Sample).FullName);
verifier.DebugPrint();
EDIT
From MSDN Page, AppDomainSetup.SetConfigurationBytes Method
Some consumers of configuration file information do not use the
information stored by the SetConfigurationBytes method. The runtime
does not enforce this. To ensure that all configuration file
information is replaced in a new application domain, use the
ConfigurationFile property to specify a configuration file. The
SetConfigurationBytes method does affect assembly binding.
So, I suspect, types in System.Configuration namespace (like ConfigurationManager) ignoring the configuration data provided via SetConfigurationBytes.
SetConfigurationBytes does work. I constructed an XDocument to represent the App.Config, as below
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="TestProbingPath" />
</assemblyBinding>
</runtime>
</configuration>
XDocument doc=as above
MemoryStream stream = new MemoryStream();
doc.Save(stream);
byte[] configBytes = stream.ToArray();
Note that stream.GetBuffer will not work.

Reading settings from app.config or web.config in .NET

I'm working on a C# class library that needs to be able to read settings from the web.config or app.config file (depending on whether the DLL is referenced from an ASP.NET web application or a Windows Forms application).
I've found that
ConfigurationSettings.AppSettings.Get("MySetting")
works, but that code has been marked as deprecated by Microsoft.
I've read that I should be using:
ConfigurationManager.AppSettings["MySetting"]
However, the System.Configuration.ConfigurationManager class doesn't seem to be available from a C# Class Library project.
What is the best way to do this?
For a sample app.config file like below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="countoffiles" value="7" />
<add key="logfilelocation" value="abc.txt" />
</appSettings>
</configuration>
You read the above application settings using the code shown below:
using System.Configuration;
You may also need to also add a reference to System.Configuration in your project if there isn't one already. You can then access the values like so:
string configvalue1 = ConfigurationManager.AppSettings["countoffiles"];
string configvalue2 = ConfigurationManager.AppSettings["logfilelocation"];
You'll need to add a reference to System.Configuration in your project's references folder.
You should definitely be using the ConfigurationManager over the obsolete ConfigurationSettings.
Update for .NET Framework 4.5 and 4.6; the following will no longer work:
string keyvalue = System.Configuration.ConfigurationManager.AppSettings["keyname"];
Now access the Setting class via Properties:
string keyvalue = Properties.Settings.Default.keyname;
See Managing Application Settings for more information.
Right click on your class library, and choose the "Add References" option from the Menu.
And from the .NET tab, select System.Configuration. This would include the System.Configuration DLL file into your project.
I'm using this, and it works well for me:
textBox1.Text = ConfigurationManager.AppSettings["Name"];
Read From Config:
You'll need to add a reference to the configuration:
Open "Properties" on your project
Go to "Settings" Tab
Add "Name" and "Value"
Get Value with using following code:
string value = Properties.Settings.Default.keyname;
Save to the configuration:
Properties.Settings.Default.keyName = value;
Properties.Settings.Default.Save();
You must add a reference to the System.Configuration assembly to the project.
You might be adding the App.config file to a DLL file. App.Config works only for executable projects, since all the DLL files take the configuration from the configuration file for the EXE file being executed.
Let's say you have two projects in your solution:
SomeDll
SomeExe
Your problem might be related to the fact that you're including the app.config file to SomeDLL and not SomeExe. SomeDll is able to read the configuration from the SomeExe project.
Try this:
string keyvalue = System.Configuration.ConfigurationManager.AppSettings["keyname"];
In the web.config file this should be the next structure:
<configuration>
<appSettings>
<add key="keyname" value="keyvalue" />
</appSettings>
</configuration>
Step 1: Right-click on references tab to add reference.
Step 2: Click on Assemblies tab
Step 3: Search for 'System.Configuration'
Step 4: Click OK.
Then it will work.
string value = System.Configuration.ConfigurationManager.AppSettings["keyname"];
I had the same problem. Just read them this way:
System.Configuration.ConfigurationSettings.AppSettings["MySetting"]
web.config is used with web applications. web.config by default has several configurations required for the web application. You can have a web.config for each folder under your web application.
app.config is used for Windows applications. When you build the application in Visual Studio, it will be automatically renamed to <appname>.exe.config and this file has to be delivered along with your application.
You can use the same method to call the app settings values from both configuration files:
System.Configuration.ConfigurationSettings.AppSettings["Key"]
As I found the best approach to access application settings variables in a systematic way by making a wrapper class over System.Configuration as below
public class BaseConfiguration
{
protected static object GetAppSetting(Type expectedType, string key)
{
string value = ConfigurationManager.AppSettings.Get(key);
try
{
if (expectedType == typeof(int))
return int.Parse(value);
if (expectedType == typeof(string))
return value;
throw new Exception("Type not supported.");
}
catch (Exception ex)
{
throw new Exception(string.Format("Config key:{0} was expected to be of type {1} but was not.",
key, expectedType), ex);
}
}
}
Now we can access needed settings variables by hard coded names using another class as below:
public class ConfigurationSettings:BaseConfiguration
{
#region App setting
public static string ApplicationName
{
get { return (string)GetAppSetting(typeof(string), "ApplicationName"); }
}
public static string MailBccAddress
{
get { return (string)GetAppSetting(typeof(string), "MailBccAddress"); }
}
public static string DefaultConnection
{
get { return (string)GetAppSetting(typeof(string), "DefaultConnection"); }
}
#endregion App setting
#region global setting
#endregion global setting
}
Also, you can use Formo:
Configuration:
<appSettings>
<add key="RetryAttempts" value="5" />
<add key="ApplicationBuildDate" value="11/4/1999 6:23 AM" />
</appSettings>
Code:
dynamic config = new Configuration();
var retryAttempts1 = config.RetryAttempts; // Returns 5 as a string
var retryAttempts2 = config.RetryAttempts(10); // Returns 5 if found in config, else 10
var retryAttempts3 = config.RetryAttempts(userInput, 10); // Returns 5 if it exists in config, else userInput if not null, else 10
var appBuildDate = config.ApplicationBuildDate<DateTime>();
If your needing/wanting to use the ConfigurationManager class...
You may need to load System.Configuration.ConfigurationManager by Microsoft via NuGet Package Manager
Tools->NuGet Package Manager->Manage NuGet Packages for Solution...
Microsoft Docs
One thing worth noting from the docs...
If your application needs read-only access to its own configuration,
we recommend that you use the GetSection(String) method. This method
provides access to the cached configuration values for the current
application, which has better performance than the Configuration
class.
I strongly recommend you to create a wrapper for this call. Something like a ConfigurationReaderService and use dependency injection to get this class. This way you will be able to isolate this configuration files for test purposes.
So use the ConfigurationManager.AppSettings["something"]; suggested and return this value. With this method you can create some kind of default return if there isn't any key available in the .config file.
Just for completeness, there's another option available for web projects only:
System.Web.Configuration.WebConfigurationManager.AppSettings["MySetting"]
The benefit of this is that it doesn't require an extra reference to be added, so it may be preferable for some people.
I always create an IConfig interface with typesafe properties declared for all configuration values. A Config implementation class then wraps the calls to System.Configuration. All your System.Configuration calls are now in one place, and it is so much easier and cleaner to maintain and track which fields are being used and declare their default values. I write a set of private helper methods to read and parse common data types.
Using an IoC framework you can access the IConfig fields anywhere your in application by simply passing the interface to a class constructor. You're also then able to create mock implementations of the IConfig interface in your unit tests so you can now test various configuration values and value combinations without needing to touch your App.config or Web.config file.
Please check the .NET version you are working on. It should be higher than 4. And you have to add the System.Configuration system library to your application.
You can use the below line. In my case it was working:
System.Configuration.ConfigurationSettings.AppSettings["yourKeyName"]
You must take care that the above line of code is also the old version and it's deprecated in new libraries.
The ConfigurationManager is not what you need to access your own settings.
To do this you should use
{YourAppName}.Properties.Settings.{settingName}
I was able to get the below approach working for .NET Core projects:
Steps:
Create an appsettings.json (format given below) in your project.
Next create a configuration class. The format is provided below.
I have created a Login() method to show the usage of the Configuration Class.
Create appsettings.json in your project with content:
{
"Environments": {
"QA": {
"Url": "somevalue",
"Username": "someuser",
"Password": "somepwd"
},
"BrowserConfig": {
"Browser": "Chrome",
"Headless": "true"
},
"EnvironmentSelected": {
"Environment": "QA"
}
}
public static class Configuration
{
private static IConfiguration _configuration;
static Configuration()
{
var builder = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json");
_configuration = builder.Build();
}
public static Browser GetBrowser()
{
if (_configuration.GetSection("BrowserConfig:Browser").Value == "Firefox")
{
return Browser.Firefox;
}
if (_configuration.GetSection("BrowserConfig:Browser").Value == "Edge")
{
return Browser.Edge;
}
if (_configuration.GetSection("BrowserConfig:Browser").Value == "IE")
{
return Browser.InternetExplorer;
}
return Browser.Chrome;
}
public static bool IsHeadless()
{
return _configuration.GetSection("BrowserConfig:Headless").Value == "true";
}
public static string GetEnvironment()
{
return _configuration.GetSection("EnvironmentSelected")["Environment"];
}
public static IConfigurationSection EnvironmentInfo()
{
var env = GetEnvironment();
return _configuration.GetSection($#"Environments:{env}");
}
}
public void Login()
{
var environment = Configuration.EnvironmentInfo();
Email.SendKeys(environment["username"]);
Password.SendKeys(environment["password"]);
WaitForElementToBeClickableAndClick(_driver, SignIn);
}
Another possible solution:
var MyReader = new System.Configuration.AppSettingsReader();
string keyvalue = MyReader.GetValue("keyalue",typeof(string)).ToString();
I have been trying to find a fix for this same issue for a couple of days now. I was able to resolve this by adding a key within the appsettings tag in the web.config file. This should override the .dll file when using the helper.
<configuration>
<appSettings>
<add key="loginUrl" value="~/RedirectValue.cshtml" />
<add key="autoFormsAuthentication" value="false"/>
</appSettings>
</configuration>
extra : if you are working on a Class Library project you have to embed the settings.json file.
A class library shouldn't really be directly referencing anything in
app.config - the class doesn't have an app.config, because it's not an
application, it's a class.
Go to the JSON file's properties.
Change Build Action -> Embedded resource.
Use the following code to read it.
var assembly = Assembly.GetExecutingAssembly();
var resourceStream = assembly.GetManifestResourceStream("Assembly.file.json");
string myString = reader.ReadToEnd();
now we have a JSON string we can Deserialize it using JsonConvert
if you didn't embed the file inside the assembly you can't use only the DLL file without the file
I'm using Visual Studio for Mac version 17.0.6.
As you can see on this screenshot it is not possible to add a reference to System.Configuration.
Solution:
install NuGet Package - System.Configuration.ConfigurationManager.
Create app.config file and set "Build action" to "EmbeddedResource"
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="name" value="Joe"/>
</appSettings>
</configuration>
using System.Configuration;
enjoy)
string name = ConfigurationManager.AppSettings["name"];
BTW: Do not add an app.config for a library
I found the answer in this link https://stackoverflow.com/a/1836938/1492229
It's not only necessary to use the namespace System.Configuration. You have also to add the reference to the assembly System.Configuration.dll , by
Right-click on the References / Dependencies
Choose Add Reference
Find and add System.Configuration.
This will work for sure.
Also for the NameValueCollection you have to write:
using System.Collections.Specialized;
Here's an example: App.config
<applicationSettings>
<MyApp.My.MySettings>
<setting name="Printer" serializeAs="String">
<value>1234 </value>
</setting>
</MyApp.My.MySettings>
</applicationSettings>
Dim strPrinterName as string = My.settings.Printer

Categories