ConfigurationDelementCollection avec un certain nombre de configurationSElements de type différent
-
29-10-2019 - |
Question
Est-il possible d'avoir une CollectionElementCollection avec un certain nombre de éléments de collecte de types différents, par exemple:
<collection>
<add type="MyType1, MyLib" Type1SpecificProp="1" />
<add type="MyType2, MyLib" Type2SpecificProp="2" />
</collection
J'ai tous les cours requis pour une telle solution:
class MyCollection : ConfigurationElementCollection { }
class MyElement : ConfigurationElement { }
class MyType1 : MyElement { }
class MyType2 : MyElement { }
...
etc
Mais quand je commence mon application, je reçois la prochaine erreur prévisible:
Attribut non reconnu «Type1Specificprop».
car Type1SpecificProp
est défini dans MyType1
ne pas MyElement
, en particulier si MyCollection
a la méthode suivante:
protected override ConfigurationElement CreateNewElement()
{
return new MyElement(); // but I want instantiate not the base class but by a type given
}
IE Renvoie la classe de base ainsi OnDeserializeUnrecognizedAttribute()
chez l'enfant classé ne sont jamais appelés.
La question est donc: comment laisser les classes enfants à résoudre des éléments inconnus par eux-mêmes?
La solution 2
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T>
De ENTLIB5, faites ce travail comme un charme.
Autres conseils
J'ai également regardé cela. PolymorphicConfigurationElementCollection<T>
semble déprécié. Edit: Ce n'est pas le cas, voir le commentaire de 'Abatishchev' ci-dessous, je reliais juste une ancienne version.
La solution de Rest Wing était prometteuse mais a malheureusement nécessité invoquer des méthodes internes résidant dans un autre espace de noms. Bien que cela soit possible via la réflexion, il ne recevra pas de prix pour coder la beauté dans ce cas.
J'ai également creusé dans la source avec réflexion et j'ai trouvé la solution suivante:
[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; }
}
}
Le remplacement de la collectionType est requis, même si cela a été spécifié via l'attribut en haut. Lorsqu'il n'est pas remplacé, la classe de base 'CollectionType fait toujours référence à `` AdDreMoveClearmap' 'qui ne déclenchera pas la fonction `` CreateEwElement (String ElementName)', c'est sa variante sans paramètre 'CreenEmElement ()'. Pour la même raison, la fonction IseElementName écrasée doit être renvoyé vrai.
Notez que j'ai créé un élémentBaseConfig qui est la classe de base de MyType1Config et MyType2Config dans laquelle vous pouvez définir certains attributs partagés.
Une instance de type spécifique (MyType1
et MyType2
) doit être créé pour que les classes d'enfants résolvent par elles-mêmes des éléments ou des attributs inconnus.
Depuis le CreateNewElement
La méthode ne donne aucune information sur les attributs d'un élément, ce qui n'est pas un lieu où un type d'instanciation spécifique peut se produire.
Après avoir creusé via le réflecteur, on vient de la pile d'appels partielle suivante:
VariantCollection.MyCollection.CreateNewElement()
System.Configuration.ConfigurationElementCollection.CallCreateNewElement()
System.Configuration.ConfigurationElementCollection.OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
La OnDeserializeUnrecognizedElement
la méthode peut être remplacée dans MyCollection
classe afin de créer une instance de type spécifique. Au lieu d'utiliser le paramètre sans paramètre CallCreateNewElement
Méthode, utilisez une nouvelle qui reçoit XmlReader
:
- Attribut de lecture
type
(assurer son existence et sa validité). - Créez un nouvel élément de type spécifié.
- Appel
internal virtual void AssociateContext( BaseConfigurationRecord configRecord )
méthode deSystem.Configuration.ConfigurationElement
sur l'élément. - Appel
internal void CallInit()
méthode deSystem.Configuration.ConfigurationElement
sur l'élément. - Retourner l'élément préparé.
BTW, s'il n'y aura pas trop d'éléments de collecte différents, pensez à utiliser quelque chose comme:
<myType1Collection>
<add Type1SpecificProp="1" />
</myType1Collection>
<myType2Collection>
<add Type2SpecificProp="2" />
</myType2Collection>
De cette façon, vous pouvez éviter de lancer les articles de MyCollection
à un type spécifique.