I have created a custom ConfigurationSection, ConfigurationElement(s) and ConfigurationElementCollection(s) for my app.config, it's based on this documentation.
Now I would like to be able to access a parent element in any configuration element.
For example something in the lines of the following:
public class CustomSection : ConfigurationSection
{
[ConfigurationProperty("child")]
public ChildElement Child
{
get { return (ChildElement)this["child"]; }
set { this["child"] = value; }
}
}
public class ChildElement : ConfigurationElement
{
[ConfigurationProperty("name")]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("nestedchild")]
public NestedChildElement NestedChild
{
get { return (NestedChildElement)this["nestedchild"]; }
set { this["nestedchild"] = value; }
}
}
public class NestedChildElement : ConfigurationElement
{
[ConfigurationProperty("name")]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
public void Sample()
{
// How can I access parent ChildElement object
// and also its parent CustomSection object from here?
}
}
Is there something in the base ConfigurationElement class that I'm missing and that would enable me to do this?
I'm hoping if it's possible to achieve this with some kind of generic solution;
One that would not require to introduce something like a Parent property on each element and then need to assign that property value in each ConfigurationProperty getter.
You haven't missed anything in ConfigurationElement, it's unable to give you any hierarchy or order information. You'll have to keep this information yourself, for example see this answer.
For a generic solution you may want to check out my POC for defining parent placeholders in app.config.
As a side note, in previous version I've done this with interface (you could check previous commits) and in the current version with extension property.
Also, the following is a trimmed down version that accomplishes just your requirement:
public abstract class ConfigurationElementBase : ConfigurationElement
{
protected T GetElement<T>(string name) where T : ConfigurationElement
=> this.GetChild<T>(name);
}
public abstract class ConfigurationSectionBase : ConfigurationSection
{
protected T GetElement<T>(string name) where T : ConfigurationElement
=> this.GetChild<T>(name);
}
public static class ConfigurationExtensions
{
private static readonly Dictionary<ConfigurationElement, ConfigurationElement> Parents =
new Dictionary<ConfigurationElement, ConfigurationElement>();
public static T GetParent<T>(this ConfigurationElement element) where T : ConfigurationElement
=> (T)Parents[element];
private static void SetParent(this ConfigurationElement element, ConfigurationElement parent)
=> Parents.Add(element, parent);
private static object GetValue(this ConfigurationElement element, string name)
=> element.ElementInformation.Properties.Cast<PropertyInformation>().First(p => p.Name == name).Value;
internal static T GetChild<T>(this ConfigurationElement element, string name) where T : ConfigurationElement
{
T childElement = (T)element.GetValue(name);
if (!Parents.ContainsKey(childElement))
childElement.SetParent(element);
return childElement;
}
}
Now you can use these base configuration classes in your custom section as following:
public class CustomSection : ConfigurationSectionBase
{
[ConfigurationProperty("name")]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("child")]
public ChildElement Child => base.GetElement<ChildElement>("child");
}
public class ChildElement : ConfigurationElementBase
{
[ConfigurationProperty("name")]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("nestedchild")]
public NestedChildElement NestedChild => base.GetElement<NestedChildElement>("nestedchild");
}
public class NestedChildElement : ConfigurationElement
{
[ConfigurationProperty("name")]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
public void Sample()
{
ChildElement parentChildElement = this.GetParent<ChildElement>();
CustomSection parentCustomSection = parentChildElement.GetParent<CustomSection>();
// TODO Use the parents ...
}
Related
Is it possible to use a Template from a specific class in another class?
Example:
class DBObject<DataType>
{
private string _name;
private DataType _value;
public DBObject()
{
_name = String.Empty;
}
public DBObject(string name)
{
this._name = name;
}
public DataType GetValue
{
get { return _value; }
set { _value = value; }
}
}
DataType is the Template I'm using in the class DBObject
My TestTbl Class contains the DBObject<DataType> elements.
class TestTbl : DBTableObjects
{
public DBObject<int> _id;
DBObject<string> _name;
DBObject<string> _address;
TestTbl()
{
AddDBTableObject(_id = new DBObject<int>("id"));
}
}
The AddDBTableObject function adds the element to a List in the DBTableObjects Class.
class DBTableObjects
{
List<DBObject> ls;
public DBTableObjects()
{
ls = new List<DBObject>();
}
protected void AddDBTableObject(DBObject obj)
{
ls.Add(obj);
}
}
Problem: The List<DBObject> requires also a templatetype for the DBObject instance. How can I use the DataType from the DBObject Class here?
Problem is in List<DBObject> ls; It needs to be generic:
class DBTableObjects
{
List<DBObject<int>> ls;
public DBTableObjects()
{
ls = new List<DBObject<int>>();
}
protected void AddDBTableObject(DBObject<int> obj)
{
ls.Add(obj);
}
}
I have a two C# POCO's setup as BsonDocumentBackedClass. Individually they work as expected, if I make one a property of another the property class no longer works and I cannot set any values in it. Here is an example:
[Serializable, DataContract, BsonSerializer(typeof(VehicleStatusClassSerializer))]
public class VehicleStatus : BsonParent, IVehicleStatus
{
public VehicleStatus() : this(new BsonDocument()) { }
internal VehicleStatus(BsonDocument backingDocument) : base(backingDocument, new VehicleStatusClassSerializer()) { }
[DataMember, BsonElement]
public String IntelliStatus
{
get { return GetValue("IntelliStatus", default(String)); }
set { SetValue("IntelliStatus", value); }
}
[DataMember, BsonElement]
public String Description
{
get { return GetValue("Description", default(String)); }
set { SetValue("Description", value); }
}
[DataMember, BsonElement, BsonSerializer(typeof(GeoLocationVehicleInfoClassSerializer))]
public GeoLocationVehicleInfo VehicleInfo
{
get { return GetValue<GeoLocationVehicleInfo>("VehicleInfo", null); }
set { SetValue("VehicleInfo", value); }
}
The serializer looks like this
public class VehicleStatusClassSerializer : BsonDocumentBackedClassSerializer<VehicleStatus>
{
public VehicleStatusClassSerializer()
{
RegisterMember("IntelliStatus", "IntelliStatus", new StringSerializer());
RegisterMember("Description", "Description", new StringSerializer());
RegisterMember("VehicleInfo", "VehicleInfo", new GeoLocationVehicleInfoClassSerializer());
}
protected override VehicleStatus CreateInstance(BsonDocument backingDocument)
{
return new VehicleStatus(backingDocument);
}
}
The embedded class looks like this
[Serializable, DataContract, BsonSerializer(typeof(GeoLocationVehicleInfoClassSerializer))]
public class GeoLocationVehicleInfo : BsonDocumentBackedClass//, IGeoLocationVehicleInfo
{
public GeoLocationVehicleInfo() : this(new BsonDocument()) { }
internal GeoLocationVehicleInfo(BsonDocument backingDocument) : base(backingDocument, new GeoLocationClassSerializer())
{
}
[DataMember, BsonElement]
public Double speed {
get
{
try
{
return GetValue<Double>("speed", 0.0);
}
catch (Exception)
{
return 0.0;
}
}
set { SetValue("speed", value); }
}
}
And here is the embedded documents serializer
public class GeoLocationVehicleInfoClassSerializer : BsonDocumentBackedClassSerializer<GeoLocationVehicleInfo>
{
public GeoLocationVehicleInfoClassSerializer()
{
RegisterMember("speed", "speed", new BsonDoubleSerializer());
}
protected override GeoLocationVehicleInfo CreateInstance(BsonDocument backingDocument)
{
return new GeoLocationVehicleInfo(backingDocument);
}
}
So in the C# code if I try and do VehicleStatus.VehicleInfo.speed = 1.0. It never gets set.
I'm creating a custom configuration section but I keep getting a Attribute Not Recognized error when trying to get the section.
I'm pretty sure it's some dumb typo - hopefully someone here can spot it.
Code:
<configSections>
<section name="ClientFilterSettings" type="ESPDB.Config.ClientFilterSettings, ESPDB"/>
</configSections>
<ClientFilterSettings>
<AllowedIPs>
<IPAddress IP="255.255.255.255"/>
</AllowedIPs>
</ClientFilterSettings>
Section:
namespace ESPDB.Config
{
public class ClientFilterSettings : ConfigurationSection
{
private static ClientFilterSettings _settings =
ConfigurationManager.GetSection(typeof(ClientFilterSettings).Name) as ClientFilterSettings
/*?? new ClientFilterSettings()*/;
private const string _allowedIPs = "AllowedIPs";
public static ClientFilterSettings Settings
{
get { return _settings; }
}
[ConfigurationProperty(_allowedIPs, IsRequired = true)]
[ConfigurationCollection(typeof(IPAddressCollection))]
public IPAddressCollection AllowedIPs
{
get { return (IPAddressCollection)this[_allowedIPs]; }
set { this[_allowedIPs] = value; }
}
}
}
Collection:
namespace ESPDB.Config
{
public class IPAddressCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new IPAddressCollection();
}
protected override object GetElementKey(ConfigurationElement element)
{
return (element as IPAddressElement).IP;
}
protected override string ElementName
{
get { return "IPAddress"; }
}
public IPAddressElement this[int index]
{
get
{
return base.BaseGet(index) as IPAddressElement;
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
public new IPAddressElement this[string responseString]
{
get { return (IPAddressElement)BaseGet(responseString); }
set
{
if (BaseGet(responseString) != null)
{
BaseRemoveAt(BaseIndexOf(BaseGet(responseString)));
}
BaseAdd(value);
}
}
}
}
Element
namespace ESPDB.Config
{
public class IPAddressElement : ConfigurationElement
{
private const string _ip = "IP";
[ConfigurationProperty(_ip, IsKey = true, IsRequired = true)]
public string IP
{
get { return this[_ip] as string; }
set { this[_ip] = value; }
}
}
}
There are multiple problems in your code.
1). You are recursively creating objects of ClientFilterSettings. Remove the following code, its not required.
private static ClientFilterSettings _settings =
ConfigurationManager.GetSection(typeof(ClientFilterSettings).Name) as ClientFilterSettings /*?? new ClientFilterSettings()*/;
public static ClientFilterSettings Settings
{
get { return _settings; }
}
2). Change the attribute from
[ConfigurationCollection(typeof(IPAddressCollection))]
TO
[ConfigurationCollection(typeof(IPAddressElement), AddItemName = "IPAddress", CollectionType = ConfigurationElementCollectionType.BasicMap)]
3). You are creating collection object within a collection. Modify the code below
return new IPAddressCollection();
WITH
return new IPAddressElement();
I would like to distribute a DLL with a ConfigurationSection as follows:
public class StandardConfiguration : ConfigurationSection
{
public static StandardConfiguration GetInstance()
{
return (StandardConfiguration)ConfigurationManager.GetSection("customConfigSection");
}
[ConfigurationProperty("childConfig")]
public StandardChildConfig ChildConfig
{
get { return (StandardChildConfig)this["childConfig"]; }
set { this["childConfig"] = value; }
}
}
public class StandardChildConfig : ConfigurationElement
{
[ConfigurationProperty("p1")]
public string P1
{
get { return (string)this["p1"]; }
set { this["p1"] = value; }
}
}
I would like to make the ConfigurationSection and its child ConfigElement inheritable. This can be done using a type parameter as follows:
public class StandardConfiguration<TChildConfig> : ConfigurationSection
where TChildConfig : StandardChildConfig
{
[ConfigurationProperty("childConfig")]
public TChildConfig ChildConfig
{
get { return (TChildConfig)this["childConfig"]; }
set { this["childConfig"] = value; }
}
}
public class StandardChildConfig : ConfigurationElement
{
[ConfigurationProperty("p1")]
public string P1
{
get { return (string)this["p1"]; }
set { this["p1"] = value; }
}
}
However, I think this would prevent me from having a static Instance to reference from other classes in my DLL because I would not know the ultimate type of child ConfigurationElement.
Any ideas or suggestions on how to implement this more cleanly are welcome.
Thanks.
EDIT
Assuming that there is a <customConfigSection> in the application's configuration, I can use StandardConfiguration.GetInstance().ChildConfig.P1 to access the P1 value in the first scenario. How would I access that same value in the second scenario? How would I implement GetInstance()?
EDIT 2
Below is the "zero-coding" scenario:
<?xml version="1.0"?>
<configuration>
<configSections>
<section
name="customConfig"
type="WebsiteTemplate.Config.StandardConfigruation, WebsiteTemplate"
/>
</configSections>
<customConfig baseProp1="a">
<childConfig baseProp2="b" />
</customConfig>
</configuration>
And here is the scenario where the configuration was extended:
<?xml version="1.0"?>
<configuration>
<configSections>
<section
name="customConfig"
type="WebsiteTemplate.Extended.Config.ExtendedConfigruation, WebsiteTemplate.Extended"
/>
</configSections>
<customConfig baseProp1="a" extendedProp1="c">
<childConfig baseProp2="b" extendedProp2="d" />
</customConfig>
</configuration>
In the second instance StandardConfiguration.GetInstance() doesn't make any sense because StandardConfiguraiton is generic. You'd have to use StandardConfiguration<MyChildConfig>.GetInstance().ChildConfig.P1
You might be able to do something like this:
public class StandardConfigurationBase : ConfigurationSection
{
public static StandardConfigurationBase GetInstance()
{
return (StandardConfigurationBase) ConfigurationManager.GetSection("customConfigSection");
}
[ConfigurationProperty("childConfig")]
public StandardChildConfig ChildConfig
{
get { return (StandardChildConfig) this["childConfig"]; }
set { this["childConfig"] = value; }
}
}
public class StandardConfiguration<TChildConfig> : StandardConfigurationBase
where TChildConfig : StandardChildConfig
{
[ConfigurationProperty("childConfig")]
public new TChildConfig ChildConfig
{
get { return (TChildConfig)this["childConfig"]; }
set { this["childConfig"] = value; }
}
}
public class StandardChildConfig : ConfigurationElement
{
[ConfigurationProperty("p1")]
public string P1
{
get { return (string)this["p1"]; }
set { this["p1"] = value; }
}
}
Then access the child when its specific type is not known:
StandardConfigurationBase b = new StandardConfiguration<StandardChildConfig>();
StandardChildConfig x = StandardConfigurationBase.GetInstance().ChildConfig;
But, I'm unclear of the real value in doing this.
The "answer" to my question is to break the base config up into an abstract class with type parameters and an interface.
Below shows what is defined in the BaseLib.dll. There is a default configuration and default child configuration.
Interface and absract class
public interface IAppConfig
{
string AppProp1 { get; }
SubConfig SubConfig { get; }
}
public abstract class BaseAppConfig<TSubConfig> : ConfigurationSection, IAppConfig
where TSubConfig : SubConfig
{
[ConfigurationProperty("appProp1")]
public string AppProp1
{
get { return (string)this["appProp1"]; }
set { this["appProp1"] = value; }
}
[ConfigurationProperty("subConfig")]
public TSubConfig SubConfig
{
get { return (TSubConfig)this["subConfig"]; }
set { this["subConfig"] = value; }
}
// Implement the interface
string IAppConfig.AppProp1 { get { return this.AppProp1; } }
SubConfig IAppConfig.SubConfig { get { return this.SubConfig; } }
}
Default implementations
public class AppConfig : BaseAppConfig<SubConfig>
{
const string SECTION_KEY = "AppConfig";
public static IAppConfig Instance
{
get { return (IAppConfig)ConfigurationManager.GetSection(SECTION_KEY); }
}
}
public class SubConfig : ConfigurationElement
{
[ConfigurationProperty("supProp1")]
public string SubProp1
{
get { return (string)this["supProp1"]; }
set { this["supProp1"] = value; }
}
}
How the config is accessed from BaseLib.dll
public class ArbitraryClass
{
void DoSometing()
{
Console.Write(AppConfig.Instance.SubConfig.SubProp1);
}
}
Below shows what is defined in the ExtLib.dll. Both the configuration and child configuration are extended.
Extended implementations
public class ExtAppConfig : BaseAppConfig<ExtSubConfig>
{
public static ExtAppConfig Instance
{
get { return (ExtAppConfig)AppConfig.Instance; }
}
[ConfigurationProperty("extAppProp1")]
public string ExtAppProp1
{
get { return (string)this["extAppProp1"]; }
set { this["extAppProp1"] = value; }
}
}
public class ExtSubConfig : SubConfig
{
[ConfigurationProperty("extSubProp1")]
public string ExtSubProp1
{
get { return (string)this["extSubProp1"]; }
set { this["extSubProp1"] = value; }
}
}
How the config is accessed from ExtLib.dll
public class ExtArbitraryClass
{
void DoSometing()
{
Console.Write(ExtAppConfig.Instance.SubConfig.ExtSubProp1);
}
}
There is a little more defined in the library, but it should make extending this configuration relatively easy.
I got an abstract class :
abstract class ClassBase
{
public abstract string Test { get; }
}
I want to derive it and by the way add a set accesor
class ClassDerive : ClassBase
{
string _s;
public override string Test
{
get { return _s; }
set { _s = value; }
}
}
I can't do that because i may not override set
class ClassDerive2 : ClassBase
{
string _s;
public string Test
{
override get { return _s; }
set { _s = value; }
}
}
Syntax error
class ClassDerive3 : ClassBase
{
string _s;
public override string ClassBase.Test
{
get { return _s; }
}
public string Test
{
set { _s = value; }
}
}
Syntax error
Any Idea ???
thx
You cannot do exactly what you want to do but here is a workaround:
abstract class ClassBase
{
public abstract String Test { get; }
}
class ClassDerive : ClassBase
{
string _s;
public override string Test
{
get { return _s; }
}
public void SetTest(String test)
{
this._s = test;
}
}
This will make Test only settable in ClassDerived via the public SetTest method. I know this is not as clean as using the property's setter but it is about as good as it's going to get.
If at first you have defined a read-only property in a type, you can't later change it to a read/write property in a derived class. That's simply how .NET works, and can't be changed.
If, on the other hand, you define an interface with a read-only property, you can later implement that interface in a class with a writable property.
If you'd like to share what you are trying to achieve, perhaps we can come up with a design that works and can compile :)
Another way:
abstract class ClassBase
{
public abstract string Test { get; }
}
class ClassDerive : ClassBase
{
string _s;
protected void setTest(string s)
{
_s = s;
}
public override string Test
{
get { return _s; }
}
}
class ClassDerive2 : ClassDerive
{
public new string Test
{
get { return base.Test; }
set { setTest(value); }
}
}
class Program
{
static void Main(string[] args)
{
var cd2 = new ClassDerive2();
cd2.Test = "asdf";
Console.WriteLine(cd2.Test);
}
}
My first thought was also to implement it as an interface. If this fits in with your design, the following code will work:
public interface TestInterface
{
string TestProperty { get; }
}
public class TestClass : TestInterface
{
public string TestProperty
{
get { return "test"; }
set { string t = value; }
}
}
No you cant, sorry. It is by design, so it's the law.