Reading attributes in ConfigurationSectionGroup - c#

Consider the following configuration group in a .NET .config file.
<MySettingsGroup enabled="true">
<MySettingsSection enabled="true">
</MySettingsSection>
</MySettingsGroup>
The supporting classes are:
public class MySettingsConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("enabled", DefaultValue = true, IsRequired = false)]
public bool Enabled
{
get
{
// works fine
return Convert.ToBoolean(this["enabled"]);
}
}
public class MySettingsConfigurationGroup : ConfigurationSectionGroup
{
[ConfigurationProperty("enabled", DefaultValue = true, IsRequired = false)]
public bool Enabled
{
get
{
// the way to retrieve attributes in sections is not supported by groups
// return Convert.ToBoolean(this["enabled"]);
return true;
}
}
How can the Enabled property on the MySettingsConfigurationGroup be implemented?

I don't think section groups were designed to be customized in the way you're attempting. A better solution would be to simply define your own configuration section that itself contains other configurations and omit the use of a section group altogether. Then, you'd get the full flexibility that configuration sections offer.

Related

C# App.config on a per-user basis

I'm writing an application that has some application level configurations, but also has some configurations that should be done on a per-user basis.
I'm writing a config section for BrowserStack to use the App.config file (per msdn) which has let me define the important things that need to be stored, however, there are things that belong in a config file that don't belong in version control. In this particular instance browserstack.user and browserstack.key (which are retrieved from their documentation) belong in a separate config file and they shouldn't be checked in.
Does c# configuration allow for this kind of behavior, or would I have to modify my config section to do this? (Included below for reference)
public class BrowserStackSettings : ConfigurationSection
{
[ConfigurationProperty("hubUrl", DefaultValue = "http://hub-cloud.browserstack.com/wd/hub/", IsRequired = false)]
public string HubUrl
{
get
{
return (string)this["hubUrl"];
}
set
{
this["hubUrl"] = value;
}
}
[ConfigurationProperty("user", IsRequired = true)]
public string User
{
get
{
return (string)this["user"];
}
set
{
this["user"] = value;
}
}
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get
{
return (string)this["key"];
}
set
{
this["key"] = value;
}
}
}
C# has something called settings. The scope of one setting can be "user" or "application". Applications settings cannot be changed easily. They are stored in app's .config file in Program Files. But user settings can be changed, and of course can be different for each user. Those settings are stored in another .config file somewhere in user's profile.
So, app should somehow allow users to change those settings, and to save them just call Properties.Settings.Default.Save();

Best practices to create connection files

I have an application which i have some configuration files for cache, queue, and database.
public class ServerConfiguration: ConfigurationSection
{
[ ConfigurationProperty( FOO, DefaultValue = "", IsRequired = false ) ]
public string FOO
{
get { return (string)this[FOO]; }
set { this[FOO] = value; }
}
}
this is what i do for config files and I also have some inheritance hierarchy.
What do you use to handle configurations and what are some best practices for this purpose?
I love and use the Microsoft configuration library extensively but I try to make sure that my applications are not dependent on it. This usually involves having my configuration section implement an interface, so your example would look like:
public class ServerConfiguration : ConfigurationSection, IServerConfiguration
{
[ ConfigurationProperty( FOO, DefaultValue = "", IsRequired = false ) ]
public string FOO
{
get { return (string)this[FOO]; }
set { this[FOO] = value; }
}
}
public interface IServerConfiguration
{
public string FOO { get; } //Unless I am updating the config in code I don't use set on the interface
}
Now where ever you use your configuration in your code you only need to worry about IServerConfiguration and you can change your implementation without having to change the usages. Sometimes I just start of with a hard coded class during development and only change it to a configuration section when I actually need to have different values in different environments.
If you are using a configuration section you are also dependent on the ConfigurationManager. I have hidden this from my code by using an IConfigurationProvider[T] where T would be IServerConfiguration, you can see an example of this on my blog under configuration ignorance.
http://bronumski.blogspot.com/search/label/Configuration

Why does StringValidator always fail for custom configuration section?

I have created a custom configuration section in a c# class library by inheriting from ConfigurationSection. I reference the class library in my web application (also c#, ASP.NET), fill in the appropriate attributes and everything works great. The problem starts when I start adding validators.
For example, this property:
[ConfigurationProperty("appCode", IsRequired = true)]
public string ApplicationCode
{
get
{
return (string)base["appCode"];
}
set
{
base["appCode"] = value;
}
}
As is it works fine, but as soon as I add this:
[StringValidator(MinLength=1)]
It bombs with the following error:
The value for the property 'appCode' is not valid. The error is: The string must be at least 1 characters long.
I get this error even though a valid appCode value is in my web.config file. If I remove the validator it works perfectly. Does anyone know how to get around this?
I was able to work around this issue by using an explicit ConfigurationProperty as the key to my properties collection rather than a string, as per the following implementation:
public class AssemblyElement : ConfigurationElement
{
private static readonly ConfigurationProperty _propAssembly;
private static readonly ConfigurationPropertyCollection _properties;
static AssemblyElement()
{
_propAssembly = new ConfigurationProperty("assembly", typeof(string), null, null, new StringValidator(1), ConfigurationPropertyOptions.IsKey | ConfigurationPropertyOptions.IsRequired);
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propAssembly);
}
internal AssemblyElement() { }
public AssemblyElement(string assemblyName)
{
this.Assembly = assemblyName;
}
[ConfigurationProperty("assembly", IsRequired = true, IsKey = true, DefaultValue = "")]
[StringValidator(MinLength = 1)]
public string Assembly
{
get { return (string)base[_propAssembly]; }
set { base[_propAssembly] = value; }
}
internal AssemblyName AssemblyName
{
get { return new AssemblyName(this.Assembly); }
}
protected override ConfigurationPropertyCollection Properties
{
get { return _properties; }
}
}
(This code is closely modeled after the code reflected from the AssemblyInfo configuration element class. I still wish I didn't have to duplicate my validations, but at least this code allows me to specify a blank default value while still requiring a value to be entered.)
Seems like the answer is indeed because they don't have a default value. Seems odd, so if someone has a better answer let me know and I'll accept theirs.
I had this problem for a while, then I realized that the validators are not for making attribute or elements required, they are for validating them.
To make a property required you need to use the IsRequired and ConfigrationPropertyOptions.IsRequired, e.g.
[ConfigurationProperty("casLogoutUrl", DefaultValue = null, IsRequired = true, Options = ConfigurationPropertyOptions.IsRequired)]
[StringValidator(MinLength=10)]
Or (if using the api)
ConfigurationProperty casLoginUrl = new ConfigurationProperty("casLoginUrl", typeof(string), null, null, new StringValidator(1), ConfigurationPropertyOptions.IsRequired);
Doing this, the Configuration framework will handle the property being required itself, and the validator handles validating what's in the value. Validators are not meant for making something required.
This also works on elements to make child elements required. E.g. if you are making a custom ConfigSection with child elements and need a child element to be required. However, if you make a CustomValidator that inherits from ConfigurationValidatorBase, you need to make use of ElementInformation.IsPresent, e.g.
public override void Validate(object value)
{
CredentialConfigurationElement element = (CredentialConfigurationElement)value;
if (!element.ElementInformation.IsPresent)
return; //IsRequired is handle by the framework, don't throw error here only throw an error if the element is present and it fails validation.
if (string.IsNullOrEmpty(element.UserName) || string.IsNullOrEmpty(element.Password))
throw new ConfigurationErrorsException("The restCredentials element is missing one or more required Attribute: userName or password.");
}
Long story short, you are missing the options part of your attribute to make it required and shouldn't use StringValidator(MinLength=1) to make it required. In fact StringValidator(MinLength=1) is completely redundant. If you make it required it's impossible for MinLength=1 to fail without the Required failing first because if it's present, it's guaranteed to be at least 1 character long.
Change your validator to
[ConfigurationProperty("appCode", IsRequired = true, Options=ConfigurationPropertyOptions.IsRequired)]
Then ditch the string validator.
The resolving of the StringValidator can be done by any one of the following:
Removing MinLength argument
Setting MinLength = 0
Removing the StringValidator Attribute
Adding DefaultValue to the ConfigurationProperty Attribute
The Ideal definition for the property is like:
[ConfigurationProperty("title", IsRequired = true, DefaultValue = "something")]
[StringValidator(InvalidCharacters = "~!##$%^&*()[]{}/;’\"|\\"
, MinLength = 1
, MaxLength = 256)]
public string Title
{
get { return this["title"] as string; }
set { this["title"] = value; }
}

A question about writing data to a custom section?

below is my class thanks to the article found at:
URL: Derik Whittaker
My Code:
public class FavsSection : ConfigurationSection
{
public override bool IsReadOnly()
{
return base.IsReadOnly();
}
public FavsSection() // default Constructor.
{ }
[ConfigurationProperty("Items", IsRequired=true)]
public FavouritesCollection FavsItems
{
get
{
return (FavouritesCollection)(base ["Items"]);
}
}
}
[ConfigurationCollection(typeof(FavouriteElement))]
public class FavouritesCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new FavouriteElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((FavouriteElement)(element)).ItemType;
}
public FavouriteElement this[int idx]
{
get
{
return (FavouriteElement)BaseGet(idx);
}
}
public override bool IsReadOnly()
{
return base.IsReadOnly();
}
}
public class FavouriteElement : ConfigurationElement
{
[ConfigurationProperty("id", DefaultValue = "", IsKey = true, IsRequired = true)]
public string ID
{
get
{
return ((string)(base["id"]));
}
set
{
base["id"] = value;
}
}
[ConfigurationProperty("path", DefaultValue = "", IsKey = false, IsRequired = false)]
public string Path
{
get
{
return ((string)(base["path"]));
}
set
{
base["path"] = value;
}
}
}
My config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="FavouritesMenu" type="MyFileExplorer.FavsSection, MyFileExplorer" />
</configSections>
<FavouritesMenu>
<Items>
<add id="1" path="c:\foo" />
<add id="2" path="C:\foo1" />
</Items>
</FavouritesMenu>
</configuration>
As you can see I am trying to write data into my custom section called 'Favourites Menu'. I think I have got the basic gist of the idea but I don;t see how to make my next step ... something got do with the 'IsReadOnly' method? Can someone please help me fill in the blanks? Feel free to rename things to make it easier to read? I thought I would make a half decent effort before I asked for help ...
RESEARCH: StackOverFlow - SAME QUESTION!
---------- Got lost on Pike65's comment ... cannot write to collection because it is set to read only.
I presume the collection needs setting to IsReadOnly false and some helper methods are needed to add data into the collection? This part is all alittle hazy to me ...
Thanks for reading,
Ibrar
I have been doing some basic testing and to 'NO' surprise ... the above actually works. You just need to make sure that when you want to pass data to your config section, then by default they are read only. So you will need to override the 'isReadOnly()' method inorder for the above code to work.
So the solution is that the above peice of code, does work ... you just need to override an extra method to allow you to access the collection responsible for holding your element data and manipulate its contents via the properties you define in the class that extends or inherits from the Configuration Element class.
UPDATE:
The above code sample I pasted in allows you to edit what already exists in the config file inside your custom section. In order to add a new item for example like the following:
FavsSection favconfig = (FavsSection)config.GetSection("FavouritesMenu");
ToolStripMenuItem menu = (ToolStripMenuItem)returnMenuComponents("favouritesToolStripMenuItem", form);
ToolStripItemCollection items = menu.DropDownItems;
for (int i = 0; i < items.Count; i++)
{
//favconfig.FavsItems[i].ID = i.ToString();
//favconfig.FavsItems[i].Path = items[i].Text;
favconfig.FavsItems[i] = new FavouriteElement()
{
ID = i.ToString(),
Path = items[i].Text
};
}
As you can see above, I am physically adding a new 'FavouriteElement' object into the collection returned by the property 'favconfig.FavItems'. In order to to do this, one property needs extending to support this.
public FavouriteElement this[int idx]
{
get
{
return (FavouriteElement)BaseGet(idx);
}
set
{
base.BaseAdd(value);
}
}
This indexer or paramterful property as 'Jeffrey Richter' calls them needs to have it's 'Set' accessor implemented as shown above in the code snippet. I have pasted it in here as it did not take long to figure out and most of the code is changed using a template I have used from Derik Whittaker's Article. Hopefully this will alow other coders to implement something similar.
Another solution would be to simply rather than 'getting' the collection all the time that 'lassoes' together all my 'FavouriteElements', you could implement the 'set' accessor for the related property. I have not tested this but I might be worth trying out.

How to get configuration element

Helo
Can anybody explain me how to get configuration element from .config file.
I know how to handle attributes but not elements. As example, I want to parse following:
<MySection enabled="true">
<header><![CDATA[ <div> .... </div> ]]></header>
<title> .... </title>
</MySection>
My c# code looks like this so far:
public class MyConfiguration : ConfigurationSection
{
[ConfigurationProperty("enabled", DefaultValue = "true")]
public bool Enabled
{
get { return this["enabled"].ToString().ToLower() == "true" ? true : false; }
}
[ConfigurationProperty("header")]
public string header
{
???
}
}
It works with attributes, how do I do with elements (header property in above code) ?
There is another approach for doing the same thing.
We could create an element by overriding DeserializeElement method to get string value:
public class EmailTextElement : ConfigurationElement {
public string Value { get; private set; }
protected override void DeserializeElement(XmlReader reader, bool s) {
Value = reader.ReadElementContentAs(typeof(string), null) as string;
}
}
Here's a pretty good custom config section designer tool you can use (and it's free):
Configuration Section Designer
EDIT:
I was looking into MSDN and it seems that custom config sections can't do what you want, ie. getting the config value from an element. Custom config elements can contain other config elements, but the config values always come from attributes.
Maybe you can put your html snippets into other files and refer to them from the config, like this.
<MySection enabled="true">
<header filename="myheader.txt" />
<title filename="mytitle.txt" />
</MySection>
Inherit the ConfigurationElement class and override its deserialize method. Use the new class to represent elements with text content.
http://www.codeproject.com/KB/XML/ConfigurationTextElement.aspx
Working with your example, you are going to override the Deserialization of "header" in the ConfigurationElement to get the CDATA value.
<MySection enabled="true">
<header name="foo"><![CDATA[ <div> .... </div> ]]></header>
<title> .... </title>
</MySection>
public sealed class HeaderSection: ConfigurationElement {
private string __Name, __CDATA;
[ConfigurationProperty("name", IsRequired = true)]
public string Name {
get {
return this.__Name;
}
set {
this.__Name = value;
}
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value {
get {
return this.__CDATA;
}
set {
this.__CDATA = value;
}
}
protected override void DeserializeElement(System.Xml.XmlReader reader, bool s) {
this.Name = reader.GetAttribute("name").Trim();
string cdata = reader.ReadElementContentAs(typeof(string), null) as string;
this.Value = cdata.Trim();
}
}
You can use the ConfigurationManager.GetSection("SectionName") method for getting the configuration section in the config files.
I finally found one way to do it.
There is IConfigurationSectionHandler interface that allows for things I want. It requires the one to write the method
public object Create(object parent, object configContext, XmlNode section)
After it, u parse section on your own so I was able to fetch XmlElement's without a problem:
header = s["header"] != null ? s["header"].InnerText : String.Empty;
title = s["title"] != null ? s["title"].InnerText : String.Empty;
The down side of this is that interface is outdated but MSDN states that it will not be removed from future versions of the frameworks as it is used internally.
You can create a class that inherits from System.Configuration.ConfigurationElement that represents an element in your configuration section.
There's a simple example in the MSDN documentation for ConfigurationElement.
According to MSDN, in .NET 4 there's a new CurrentConfiguration property which gives you a reference to the top-level Configuration instance that represents the configuration hierarchy that the current ConfigurationElement instance belongs to.

Categories

Resources