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.
Related
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 ...
}
If I have the following:
public abstract class Parameter<T>
{
protected T value;
public virtual T Value
{
get { return value; }
set { this.value = value; }
}
protected Parameter(T startingValue)
{
value = startingValue;
}
}
public class FloatParameter : Parameter<float>
{
public FloatParameter(float startingValue) : base(startingValue){}
}
public class IntParameter : Parameter<int>
{
public override int Value
{
get { return value; }
set { this.value = value > 100 ? 100 : value; }
}
public IntParameter(int startingValue) : base (startingValue) {}
}
Is there any way to create some List<Parameter> that can contain any of the derived types? For example, something like:
// no type specified in Parameter
List<Parameter> storedParameters = new List<Parameter>();
storedParameters.Add(new FloatParameter(2f));
storedParameters.Add(new IntParameter(7));
foreach(Parameter p in storedParameters)
{
DoSomethingWithValue(p.Value);
}
Or, alternatively, if this implementation is flawed, is there a better way to do this? What I have here feels slightly naive.
The only way I see to manage such case is to have and Interface that you use to manage the generic types, something like this should work:
public interface IParameter
{
void DoSomething();
}
public abstract class Parameter<T>
{
protected T value;
public T Value
{
get { return value; }
set { this.value = value; }
}
protected Parameter(T startingValue)
{
value = startingValue;
}
}
public class FloatParameter : Parameter<float>, IParameter
{
public FloatParameter(float startingValue) : base(startingValue) { }
public void DoSomething()
{
Console.WriteLine(value);
}
}
public class IntParameter : Parameter<int>, IParameter
{
public IntParameter(int startingValue) : base(startingValue) { }
public void DoSomething()
{
Console.WriteLine(value);
}
}
Ont his case you would be able to create a List of the Interface IParameter and add there specific instances:
var list = new List<IParameter>();
list.Add(new FloatParameter(1F));
list.Add(new IntParameter(1));
foreach (var item in list)
{
item.DoSomething();
}
Try to add nongeneric interface. Here is an example:
public class Program
{
static void Main(string[] args)
{
try
{
List<IParameter> storedParameters = new List<IParameter>();
storedParameters.Add(new FloatParameter(2f));
storedParameters.Add(new IntParameter(7));
foreach (IParameter p in storedParameters)
{
Console.WriteLine(p.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public interface IParameter
{
object value { get; }
}
public class Parameter<T> : IParameter
{
public object value { get; protected set; }
public virtual T Value
{
get { return (T)value; }
set { this.value = value; }
}
protected Parameter(T startingValue)
{
value = startingValue;
}
}
public class FloatParameter : Parameter<float>
{
public FloatParameter(float startingValue) : base(startingValue){ }
}
public class IntParameter : Parameter<int>
{
public override int Value
{
get { return (int)value; }
set { this.value = value > 100 ? 100 : value; }
}
public IntParameter(int startingValue) : base (startingValue) { }
}
No, it's not possible to do it.
What you are trying to do is to have an interface (or base class) that expose a property of an undefined type, to be able to then retrieve that value and dispatch it dynamically to the proper override of DoSomethingWithValue.
What you are after is achievable defining the property as dynamic, instead of using generics.
public class Parameter
{
protected dynamic value;
public dynamic Value
{
get { return value; }
set { this.value = value; }
}
public Parameter(dynamic startingValue)
{
value = startingValue;
}
}
public class MyStuff {
public void DoStuff()
{
List<Parameter> storedParameters = new List<Parameter>();
storedParameters.Add(new Parameter(2f));
storedParameters.Add(new Parameter(7));
foreach (Parameter p in storedParameters)
{
DoSomethingWithValue(p.Value);
}
}
}
Otherwise you should look into implementing a Double dispatch.
You can do it by defining a common interface and using the visitor pattern.
public interface IParameterVisitor
{
void VisitInt(int value);
void VisitFloat(float value);
}
public interface IParameter
{
void Accept(IParameterVisitor visitor);
}
The previous implementation has to be modified a bit:
public abstract class Parameter<T> : IParameter
{
protected T value;
public virtual T Value
{
get { return value; }
set { this.value = value; }
}
protected Parameter(T startingValue)
{
value = startingValue;
}
public abstract void Accept(IParameterVisitor visitor);
}
FloatParameter will VisitFloat, and IntParameter will VisitInt
public class FloatParameter : Parameter<float>
{
public FloatParameter(float startingValue) : base(startingValue) { }
public override void Accept(IParameterVisitor visitor)
{
visitor.VisitFloat(this.value);
}
}
public class IntParameter : Parameter<int>
{
public override int Value
{
get { return value; }
set { this.value = value > 100 ? 100 : value; }
}
public override void Accept(IParameterVisitor visitor)
{
visitor.VisitInt(this.value);
}
public IntParameter(int startingValue) : base(startingValue) { }
}
And our visitor for example:
public class MyVisitor : IParameterVisitor
{
public void VisitInt(int value)
{
Console.WriteLine($"Visiting an int: {value}");
}
public void VisitFloat(float value)
{
Console.WriteLine($"Visiting a float: {value}");
}
}
Finally, the usage:
var parameters =
new List<IParameter> {new FloatParameter(0.5f), new IntParameter(1)};
var visitor = new MyVisitor();
foreach (IParameter parameter in parameters) {
parameter.Accept(visitor);
}
If you change the value to an object you will be able to set the value to whatever type you like:
class Program
{
static void Main(string[] args)
{
// no type specified in Parameter
var storedParameters = new List<ParameterBase>();
storedParameters.Add(new FloatParameter(3.5F));
storedParameters.Add(new IntParameter(7));
foreach (var p in storedParameters)
{
Console.WriteLine(p.Value);
}
}
}
public class ParameterBase
{
protected object value;
public virtual object Value
{
get { return value; }
set { this.value = value; }
}
}
public class FloatParameter : ParameterBase
{
public FloatParameter(float value)
{
Value = value;
}
}
public class IntParameter : ParameterBase
{
public IntParameter(int value)
{
Value = value;
}
}
UPDATED: Use object instead of dynamic and removed ValueType as suggested by #Pieter Witvoet
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 am not sure how this functions in C#.
I have a class:
public class FindIt()
{
}
public class FindItFrm()
{
private bool _amISet = false;
public FindItFrm()
{
}
public bool AmISet()
{
get { return _amISet; }
set { _amISet = value; }
}
}
And the class that I am working in has:
public class MyHelper() : FindIt
{
// Constructor
public MyHelper()
: base(new FindItFrm())
}
I want to set a property (AmISet) in the FindItFrm while inside the MyHelper class. How would I accomplish this?
Your code should look like this (in the future, please post working code for examples):
public class FindIt
{
// You need to let your derived class access the FindItFrm
protected FindItFrm Frm;
// Constructor needs to accept a FindItFrm
public FindIt(FindItFrm frm)
{
Frm = frm;
}
}
public class FindItFrm
{
private bool _amISet = false;
public bool AmISet
{
get { return _amISet; }
set { _amISet = value; }
}
}
public class MyHelper : FindIt
{
// Constructor
public MyHelper()
: base(new FindItFrm())
{
Frm.AmISet = true;
}
}