I'm trying to create a Custom Section in the app.config and run into the following exception:
ConfigurationErrorsException
Unrecognized element 'EncryptedUserCredential'. (C:\My Documents\Hachette.CRM\test_app_appsettings\bin\Debug\test_app_appsettings.vshost.exe.Config line 11)
Now I'm at a complete loss. I have stepped into the RegisterEncryptedUserCredentialsConfig.GetConfig(), and found the Section is null from the propery RegisterEncryptedUserCredentialsConfig.EncryptedUserCredentials. I have also checked all the usual suspects I when investigating online, like:
Making sure the type includes the assembly name in the app.config configSection when declaring the custom section.
is at the beginning of in the app.config.
The custom section is written in:
Class Library
C#/.NET 4.0.
I'm at a loss and think I have been staring at it too long the weekend gone and need some fresh eyes!
For ease I have added all the code from the C# class library here.
Here is the app.config:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="EncryptedUserCredentials"
type="Hachette.Common.CustomConfigSections.RegisterEncryptedUserCredentialsConfig, Hachette.Common"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<EncryptedUserCredentials>
<EncryptedUserCredential userName="garethB" password="1235#"/>
<EncryptedUserCredential userName="webService" password="123ffff5#"/>
</EncryptedUserCredentials>
</configuration>
Here is the EncryptedUserCredential ConfigurationElement:
public class EncryptedUserCredential : ConfigurationElement
{
[ConfigurationProperty("userName", IsRequired = true)]
public string UserName
{
get
{
return this["userName"] as string;
}
}
[ConfigurationProperty("password", IsRequired = true)]
public string Password
{
get
{
return this["password"] as string;
}
}
}
Here is the EncryptedCredentials ConfigurationElementCollection:
public class EncryptedUserCredentials : ConfigurationElementCollection
{
public EncryptedUserCredential this[int index]
{
get
{
return base.BaseGet(index) as EncryptedUserCredential;
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
public new EncryptedUserCredential this[string responseString]
{
get
{
return (EncryptedUserCredential)BaseGet(responseString);
}
set
{
if (BaseGet(responseString) != null)
{
BaseRemoveAt(BaseIndexOf(BaseGet(responseString)));
}
BaseAdd(value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new EncryptedUserCredential();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((EncryptedUserCredential)element).UserName;
}
}
Here is the RegisterEncryptedUserCredentialsConfig ConfigurationSection:
public class RegisterEncryptedUserCredentialsConfig : ConfigurationSection
{
public static RegisterEncryptedUserCredentialsConfig GetConfig()
{
//var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
return (RegisterEncryptedUserCredentialsConfig)System.Configuration.ConfigurationManager.GetSection("EncryptedUserCredentials") ?? new RegisterEncryptedUserCredentialsConfig();
}
[System.Configuration.ConfigurationProperty("EncryptedUserCredentials", IsDefaultCollection=true,IsRequired=true)]
[ConfigurationCollection(typeof(EncryptedUserCredentials), AddItemName="EncryptedUserCredential")]
public EncryptedUserCredentials EncryptedUserCredentials
{
get
{
object o = this["EncryptedUserCredentials"];
return o as EncryptedUserCredentials;
}
}
}
All of the above live in namespace:
namespace Hachette.Common.CustomConfigSections
And in the following Assembly:
It's not possible for the root element to be the collection holder. So you should code up your configuration to match this structure (note I've picked a name for the root element to match your namespace, but feel free to choose anything you like):
<hachette>
<EncryptedUserCredentials>
<EncryptedUserCredential userName="garethB" password="1235#"/>
<EncryptedUserCredential userName="webService" password="123ffff5#"/>
</EncryptedUserCredentials>
</hachette>
This means your configuration hierarchy will have a root ConfigSection which in turn contains a ConfigurationElementCollection that contains all of the ConfigurationElement objects.
Here is an example article on how you can write it: http://www.abhisheksur.com/2011/09/writing-custom-configurationsection-to.html
Related
I am getting following exception when I run the code:
FileNotFoundException: Could not load file or assembly 'My' or one of its dependencies. The system cannot find the file specified.
Following is my app.config:
<configuration>
<configSections>
<section name="registerCompanies" type="My.MyConfigSection, My" />
</configSections>
<registerCompanies>
<add name="Tata Motors" code="Tata"/>
<add name="Honda Motors" code="Honda"/>
</registerCompanies>
</configuration>
Following is my namespace and class:
using System.Configuration;
using System.Linq;
namespace My
{
public class MyConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
public MyConfigInstanceCollection Instances
{
get { return (MyConfigInstanceCollection)this[""]; }
set { this[""] = value; }
}
}
public class MyConfigInstanceCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new MyConfigInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
//set to whatever Element Property you want to use for a key
return ((MyConfigInstanceElement)element).Name;
}
public new MyConfigInstanceElement this[string elementName]
{
get
{
return this.OfType<MyConfigInstanceElement>().FirstOrDefault(item => item.Name == elementName);
}
}
}
public class MyConfigInstanceElement : ConfigurationElement
{
//Make sure to set IsKey=true for property exposed as the GetElementKey above
[ConfigurationProperty("name", IsKey = true, IsRequired = true)]
public string Name
{
get { return (string)base["name"]; }
set { base["name"] = value; }
}
[ConfigurationProperty("code", IsRequired = true)]
public string Code
{
get { return (string)base["code"]; }
set { base["code"] = value; }
}
}
}
I am trying to retrieve the app.config using the below code:
MyConfigSection config = ConfigurationManager.GetSection("registerCompanies") as MyConfigSection;
Console.WriteLine(config.Instances["Honda Motors"].Code);
foreach (MyConfigInstanceElement e in config.Instances)
{
Console.WriteLine("Name: {0}, Code: {1}", e.Name, e.Code);
}
The only reason I think it's not working is may be because my namespace My is in another .cs file ConfigSetup.cs and I am trying to retrieve the appconfig in another .cs file.
I am not sure how to address in the below line that My.MyCofigSection exists in ConfigSetup.cs and the project name is FileManager if I need to include that as well ? Below line gives me an exception that I mentioned above.
<section name="registerCompanies" type="My.MyConfigSection, My" />
I have changed type="My.MyConfigSection, My" to type="My.MyConfigSection, File_Manager" and it worked thank you very much
I am trying to create a custom section in my web.config. I have a custom class and a declaration section in my web.config.
when I run my code however I am getting this error "...does not inherit from System.Configuration.IConfigurationSectionHandler'."
MSDN documentation states that this Interface is now deprecated.
I don't understand what cold be happening.
FYI I used this (http://blogs.perficient.com/microsoft/2017/01/4-easy-steps-to-custom-sections-in-web-config/ as my guide to create my custom section.
Here is my web.config
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name ="BDKConfigs" type="SPS.CardDecryption.Services.BDKConfigs" allowLocation="true" allowDefinition="Everywhere" />
</configSections>
Here is my custom section in the web.config
<BDKConfigs>
<!--collection-->
<BDK>
<!--elements-->
<add name="IDTechPublicBDK" value="0123456789ABCDEFFEDCBA9876543210"/>
<add name="IDTechProdBDK" value=""/>
</BDK>
</BDKConfigs>
this is my custom class
using System.Configuration;
namespace myNameSpace
{
//This class reads the defined config section (if available) and stores it locally in the static _Config variable.
public class BDKConfigs
{
public static BDKConfigsSection _Config = ConfigurationManager.GetSection("BDKConfigs") as BDKConfigsSection;
public static BDKElementCollection GetBDKGroups()
{
return _Config.BDKConfigs;
}
}
public class BDKConfigsSection : ConfigurationSection
{
//Decorate the property with the tag for your collection.
[ConfigurationProperty("BDK")]
public BDKElementCollection BDKConfigs
{
get { return (BDKElementCollection)this["BDK"]; }
}
}
//Extend the ConfigurationElementCollection class.
//Decorate the class with the class that represents a single element in the collection.
[ConfigurationCollection(typeof(BDKElement))]
public class BDKElementCollection : ConfigurationElementCollection
{
public BDKElement this[int index]
{
get { return (BDKElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new BDKElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((BDKElement)element).Name;
}
}
//Extend the ConfigurationElement class. This class represents a single element in the collection.
//Create a property for each xml attribute in your element.
//Decorate each property with the ConfigurationProperty decorator. See MSDN for all available options.
public class BDKElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("queryString", IsRequired = false)]
public string Value
{
get { return (string)this["Value"]; }
set { this["Value"] = value; }
}
}
}
The issue was that I had the wrong type specified in the web.config. When I changed my web.config to
type="SPS.CardDecryption.Services.BDKConfigsSection , worked like a charm.
I have updated answer:
public class BDKConfigsSection : ConfigurationSection
{
[ConfigurationProperty("BDK")]
public BDKElementCollection BDKConfigs
{
get { return (BDKElementCollection) base["BDK"]; }
}
}
[ConfigurationCollection(typeof(BDKElement))]
public class BDKElementCollection : ConfigurationElementCollection
{
public BDKElement this[int index]
{
get { return (BDKElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new BDKElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((BDKElement)element).Name;
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMapAlternate; }
}
protected override string ElementName => "BDKItem";
}
public class BDKElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("queryString", IsRequired = false)]
public string Value
{
get { return (string)this["queryString"]; }
set { this["queryString"] = value; }
}
}
that one working fine :
<BDKConfigsSection>
<BDK>
<BDKItem name="IDTechPublicBDK" queryString="0123456789ABCDEFFEDCBA9876543210"/>
<BDKItem name="IDTechProdBDK" queryString=""/>
</BDK> </BDKConfigsSection>
I want to create a custom configuration for C# application. The app.config likes:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="location" type ="FolderConfigSection.LocationConfig, FolderConfigSection"/>
</configSections>
<location>
<folders>
<add folder ="C:\Test1"/>
<add folder ="C:\Test2" />
<add folder ="C:\Test3" />
</folders>
</location>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
So I created two classes.
namespace FolderConfigSection
{
public class LocationConfig : ConfigurationSection
{
[ConfigurationProperty("folders")]
public string Folder
{
get { return base["folder"] as string; }
set { base["folder"] = value; }
}
}
}
And
namespace FolderConfigSection
{
public class FolderCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new FolderElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((FolderElement)element).Environment;
}
}
public class FolderElement : ConfigurationElement
{
[ConfigurationProperty("folder", IsRequired = true)]
public string Environment
{
get { return (string)this["folder"]; }
set { this["folder"] = value; }
}
}
}
But I got an exception in my Program.cs
public class Program
{
public static void Main(string[] args)
{
LocationConfig _locationConfig = (LocationConfig)ConfigurationManager.GetSection("location");
string firstFolder = _locationConfig.Folder;
}
}
The exception is The section name 'location' is reserved for <location> sections.
Also I want to list all folders.
I'm restating what #Tim has answered in his comments.
Do not use location as section name. It is already reserved for <location> element configuration. Just use other name, such as locationConfig.
Change the type of Folder property in LocationConfig class from String to FolderCollection. For clarity, you should probably change the property name Folder to its plural form Folders.
To list all the folders in LocationConfig, you just need to iterate the Folders collection property. You might also want to implement indexer on FolderCollection class, for an indexed access to Folders property, .
Putting it all in code:
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="locationConfig" type ="FolderConfigSection.LocationConfig, FolderConfigSection"/>
</configSections>
<locationConfig>
<folders>
<add folder ="C:\Test1"/>
<add folder ="C:\Test2" />
<add folder ="C:\Test3" />
</folders>
</locationConfig>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
LocationConfig
namespace FolderConfigSection
{
using System.Configuration;
public class LocationConfig : ConfigurationSection
{
// Change the property type to FolderCollection, and
// change the property name to `Folders` for clarity
[ConfigurationProperty("folders")]
public FolderCollection Folders
{
get { return base["folders"] as FolderCollection; }
// the setter property isn't really needed here
// set { base["folders"] = value; }
}
}
public class FolderCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new FolderElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((FolderElement)element).Environment;
}
// indexer
public FolderElement this[int index]
{
get { return BaseGet(index) as FolderElement; }
}
}
public class FolderElement : ConfigurationElement
{
[ConfigurationProperty("folder", IsRequired = true)]
public string Environment
{
get { return (string)this["folder"]; }
set { this["folder"] = value; }
}
}
}
Program.cs
public class Program
{
static void Main(string[] args)
{
var locationConfig = (LocationConfig)ConfigurationManager.GetSection("locationConfig");
foreach (FolderElement folder in locationConfig.Folders)
{
// List all folders
Console.WriteLine(folder.Environment);
}
// The first folder using indexer property
Console.WriteLine(locationConfig.Folders[0].Environment);
}
}
I have a custom config which is based on some classes. My problem is that I get an error saying that a config element is unrecognized. The class is as follows:
[ConfigurationCollection(typeof(SectionItem), AddItemName = "Item", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class Sections : ConfigurationElementCollection
{
public SectionItem this[int index]
{
get { return BaseGet(index) as SectionItem; }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new SectionItem this[string response]
{
get { return (SectionItem)BaseGet(response); }
set
{
if (BaseGet(response) != null)
{
BaseRemoveAt(BaseIndexOf(BaseGet(response)));
}
BaseAdd(value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new SectionItem();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((SectionItem)element).Key;
}
}
And the SectionItem class:
public class SectionItem : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true, IsKey = true)]
public string Key
{
get { return this["key"] as string; }
}
}
If I add a configuration property of type SectionItems in the Sections class that won't work for me, because I want to have multiple SectonItems inside the Section tag in the config file. I've searched for solutions but everything I've found didn't do the trick with this.
For a better understanding of what I'm trying to achieve this is how my config looks:
<configuration>
<configSections>
<section name="AdminConfig" type="XmlTest.AdminConfig, XmlTest"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<AdminConfig>
<Field name="field1" key="12345" path="asd"/>
<Section>
<Item key="12345"/>
<Item key="54321"/>
</Section>
</AdminConfig>
</configuration>
OK so I've found the problem. Although the Sections class had this [ConfigurationCollection(typeof(SectionItem), AddItemName = "Item", CollectionType = ConfigurationElementCollectionType.BasicMap)] I had to annotate the property in the ConfigurationSection class, as follows:
[ConfigurationProperty("Section")]
[ConfigurationCollection(typeof(Sections), AddItemName = "Item")]
public Sections Sections
{
get
{
return (Sections)this["Section"];
}
}
Now the items are recognized and stuff is working properly.
I created a customer configuration class Reports. I then created another class called "ReportsCollection". When I try and do the "ConfigurationManager.GetSection()", it doesn't populate my collection variable. Can anyone see any mistakes in my code?
Here is the collection class:
public class ReportsCollection : ConfigurationElementCollection
{
public ReportsCollection()
{
}
protected override ConfigurationElement CreateNewElement()
{
throw new NotImplementedException();
}
protected override ConfigurationElement CreateNewElement(string elementName)
{
return base.CreateNewElement(elementName);
}
protected override object GetElementKey(ConfigurationElement element)
{
throw new NotImplementedException();
}
public Report this[int index]
{
get { return (Report)BaseGet(index); }
}
}
Here is the reports class:
public class Report : ConfigurationSection
{
[ConfigurationProperty("reportName", IsRequired = true)]
public string ReportName
{
get { return (string)this["reportName"]; }
//set { this["reportName"] = value; }
}
[ConfigurationProperty("storedProcedures", IsRequired = true)]
public StoredProceduresCollection StoredProcedures
{
get { return (StoredProceduresCollection)this["storedProcedures"]; }
}
[ConfigurationProperty("parameters", IsRequired = false)]
public ParametersCollection Parameters
{
get { return (ParametersCollection)this["parameters"]; }
}
[ConfigurationProperty("saveLocation", IsRequired = true)]
public string SaveLocation
{
get { return (string)this["saveLocation"]; }
}
[ConfigurationProperty("recipients", IsRequired = true)]
public RecipientsCollection Recipients
{
get { return (RecipientsCollection)this["recipients"]; }
}
}
public class StoredProcedure : ConfigurationElement
{
[ConfigurationProperty("storedProcedureName", IsRequired = true)]
public string StoredProcedureName
{
get { return (string)this["storedProcedureName"]; }
}
}
public class StoredProceduresCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
throw new NotImplementedException();
}
protected override ConfigurationElement CreateNewElement(string elementName)
{
return base.CreateNewElement(elementName);
}
protected override object GetElementKey(ConfigurationElement element)
{
throw new NotImplementedException();
}
public StoredProcedure this[int index]
{
get { return (StoredProcedure)base.BaseGet(index); }
}
}
}
And here is the very straight-forward code to create the variable:
ReportsCollection reportsCollection = (ReportsCollection) System.Configuration.ConfigurationManager.GetSection("ReportGroup");
EDIT
Added App.Config
<configSections>
<sectionGroup name="ReportGroup">
<section name="Reports" type="ReportsGenerator.ReportsCollection"/>
</sectionGroup>
</configSections>
<ReportGroup>
<Reports name="DailyIssues" SaveLocation="">
<StoredProcedures>
<add StoredProcedureName="RPTDailyIssues" />
<add StoredProcedureName="RPTNoIssues" />
</StoredProcedures>
<Parameters>
<add ParameterName="#FromDate" />
<add ParameterName="#ThruDate" />
</Parameters>
<Recipients>
<add RecipientName="me#mycompany.com"
</Recipients>
</Reports>
</ReportGroup>
You should check out Jon Rista's three-part series on .NET 2.0 configuration up on CodeProject.
Unraveling the mysteries of .NET 2.0 configuration
Decoding the mysteries of .NET 2.0 configuration
Cracking the mysteries of .NET 2.0 configuration
Highly recommended, well written and extremely helpful!
Also, there's a Configuration Section Designer add-in for Visual Studio which is extremely helpful for creating custom configuration sections - it features a visual designer that creates all the necessary XSD and classes in the background.
Marc
I didn't write configuration sections for awhile, but out of the top of my head your CreateNewElement() methods throw exceptions.. Make them at least return dummy entries, maybe that's the reason.. )
Also, show the reportsCollection element in your web.config.. is it registered correctly?