Konfigurationselementsammlung mit einer Anzahl von Konfigurationselementen unterschiedlichen Typs
-
29-10-2019 - |
Frage
Ist es möglich, eine CollectionElementCollection mit einer Anzahl von verschiedenen CollectionElements nach Typ zu haben, z.:
<collection>
<add type="MyType1, MyLib" Type1SpecificProp="1" />
<add type="MyType2, MyLib" Type2SpecificProp="2" />
</collection
Ich habe alle Klassen, die für eine solche Lösung benötigt werden:
class MyCollection : ConfigurationElementCollection { }
class MyElement : ConfigurationElement { }
class MyType1 : MyElement { }
class MyType2 : MyElement { }
...
etc
aber wenn ich meine Bewerbung starte, erhalte ich den nächsten vorhersehbaren Fehler:
Unbekanntes Attribut 'Type1SpecificProp'.
da Type1SpecificProp
ist definiert in MyType1
nicht MyElement
, besonders wenn MyCollection
hat die nächste Methode:
protected override ConfigurationElement CreateNewElement()
{
return new MyElement(); // but I want instantiate not the base class but by a type given
}
also.gibt also die Basisklasse zurück OnDeserializeUnrecognizedAttribute()
in Kinderklassen wurden nie angerufen.
Die Frage ist also:wie können untergeordnete Klassen unbekannte Elemente selbst auflösen?
Lösung 2
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T>
von EntLib5 machen Sie diesen Job als Zauber.
Andere Tipps
Ich habe mir das auch angesehen. PolymorphicConfigurationElementCollection<T>
scheint veraltet zu sein.Bearbeiten:ist es nicht, siehe den Kommentar von 'abatishchev' unten, ich habe nur eine alte Version verlinkt.
Die Lösung von Rest Wing war vielversprechend, erforderte aber leider den Aufruf interner Methoden, die sich in einem anderen Namespace befinden.Während dies durch Reflexion möglich ist, wird es in diesem Fall keine Preise für die Codierung von Schönheit erhalten.
Ich habe auch mit Reflexion in die Quelle gegraben und bin auf die folgende Lösung gekommen:
[ConfigurationCollection(typeof(ElementBaseConfig), CollectionType=ConfigurationElementCollectionType.BasicMap)]
public class MyTypesConfigCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
// Not used but function must be defined
return null;
}
protected override object GetElementKey(ConfigurationElement element)
{
return element;
}
protected override ConfigurationElement CreateNewElement(string elementName)
{
switch (elementName)
{
case "mytype1":
return new MyType1Config();
case "mytype2":
return new MyType2Config();
default:
throw new ConfigurationErrorsException(
string.Format("Unrecognized element '{0}'.", elementName));
}
}
protected override bool IsElementName(string elementName)
{
// Required to be true
return true;
}
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
}
Die Überschreibung des CollectionType ist ERFORDERLICH, auch wenn dies über das Attribut oben angegeben wurde.Wenn die Basisklasse 'CollectionType' nicht überschrieben wird, verweist sie weiterhin auf 'AddRemoveClearMap', was nicht die erforderliche Funktion 'CreateNewElement (string elementName)' auslöst, sondern die parameterlose Variante 'CreateNemElement ()'.Aus dem gleichen Grund sollte die überschriebene Funktion IsElementName true zurückgeben.
Beachten Sie, dass ich eine ElementBaseConfig erstellt habe, die die Basisklasse von MyType1Config und MyType2Config ist, in der Sie einige gemeinsame Attribute definieren können.
Eine Instanz eines bestimmten Typs (MyType1
und MyType2
) muss erstellt werden, damit untergeordnete Klassen unbekannte Elemente oder Attribute selbst auflösen können.
Seit dem CreateNewElement
die Methode gibt keine Informationen zu den Attributen eines Elements an, dh nicht an dem Ort, an dem eine bestimmte Typinstanziierung auftreten kann.
Nach einigem Graben über Reflektor kommt man zu folgendem Teilaufrufstapel:
VariantCollection.MyCollection.CreateNewElement()
System.Configuration.ConfigurationElementCollection.CallCreateNewElement()
System.Configuration.ConfigurationElementCollection.OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
Der OnDeserializeUnrecognizedElement
methode kann überschrieben werden in MyCollection
klasse, um eine bestimmte Typinstanz zu erstellen.Anstatt das parameterlose zu verwenden CallCreateNewElement
methode, verwenden Sie eine neue, die empfängt XmlReader
:
- Attribut lesen
type
(stellen Sie seine Existenz und Gültigkeit sicher). - Erstellt ein neues Element des angegebenen Typs.
- Nennen
internal virtual void AssociateContext( BaseConfigurationRecord configRecord )
methode vonSystem.Configuration.ConfigurationElement
auf dem Element. - Nennen
internal void CallInit()
methode vonSystem.Configuration.ConfigurationElement
auf dem Element. - Geben Sie das vorbereitete Element zurück.
Übrigens, wenn es nicht zu viele verschiedene Sammlungselemente gibt, sollten Sie etwas wie verwenden:
<myType1Collection>
<add Type1SpecificProp="1" />
</myType1Collection>
<myType2Collection>
<add Type2SpecificProp="2" />
</myType2Collection>
Auf diese Weise können Sie vermeiden, die Gegenstände aus zu werfen MyCollection
zu einem bestimmten Typ.