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?

War es hilfreich?

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:

  1. Attribut lesen type (stellen Sie seine Existenz und Gültigkeit sicher).
  2. Erstellt ein neues Element des angegebenen Typs.
  3. Nennen internal virtual void AssociateContext( BaseConfigurationRecord configRecord ) methode von System.Configuration.ConfigurationElement auf dem Element.
  4. Nennen internal void CallInit() methode von System.Configuration.ConfigurationElement auf dem Element.
  5. 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top