Pergunta

É possível ter um CollectionElementCollection com vários CollectionElements diferentes por tipo, por exemplo:

<collection>
    <add type="MyType1, MyLib" Type1SpecificProp="1" />
    <add type="MyType2, MyLib" Type2SpecificProp="2" />
</collection

Eu tenho todas as classes necessárias para tal solução:

class MyCollection : ConfigurationElementCollection { }
class MyElement : ConfigurationElement { }
class MyType1 : MyElement { }
class MyType2 : MyElement { }
...
etc

mas quando inicio meu aplicativo, recebo o próximo erro previsível:

Atributo não reconhecido 'Type1SpecificProp'.

porque Type1SpecificProp é definido em MyType1 não MyElement, especialmente se MyCollection tem o próximo método:

protected override ConfigurationElement CreateNewElement()
{
    return new MyElement(); // but I want instantiate not the base class but by a type given
}

ou sejaretorna a classe base assim OnDeserializeUnrecognizedAttribute() em crianças classificadas nunca são chamadas.

Então a questão é:como permitir que as classes infantis resolvam elementos desconhecidos sozinhas?

Foi útil?

Solução 2

Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T> do EntLib5 faz esse trabalho como um encanto.

Outras dicas

Eu investiguei isso também. PolymorphicConfigurationElementCollection<T> parece obsoleto.Editar:não é, veja o comentário de 'abatishchev' abaixo, eu estava apenas vinculando uma versão antiga.

A solução do Rest Wing era promissora, mas infelizmente exigia a invocação de métodos internos residentes em outro namespace.Embora isso seja possível por meio da reflexão, não receberá preços pela codificação da beleza neste caso.

Pesquisei a fonte com o Reflection também e encontrei a seguinte solução:

[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; }
    }
}

A substituição do CollectionType é NECESSÁRIA, mesmo que tenha sido especificada por meio do atributo no topo.Quando não substituída, a classe base' CollectionType ainda se refere a 'AddRemoveClearMap', que não irá acionar a função necessária 'CreateNewElement (string elementName)', mas é a variante sem parâmetros 'CreateNemElement ()'.Pela mesma razão, a função IsElementName substituída deve retornar verdadeiro.

Observe que criei um ElementBaseConfig que é a classe base de MyType1Config e MyType2Config na qual você pode definir alguns atributos compartilhados.

Uma instância de tipo específico (MyType1 e MyType2) precisa ser criado para que as classes filhas resolvam elementos ou atributos desconhecidos por si mesmas.

Desde o CreateNewElement O método não fornece nenhuma informação sobre os atributos de um elemento, esse não é o local onde a instanciação de um tipo específico pode ocorrer.

Depois de algumas pesquisas via Reflector, chega-se à seguinte pilha de chamadas parcial:

VariantCollection.MyCollection.CreateNewElement()
System.Configuration.ConfigurationElementCollection.CallCreateNewElement()
System.Configuration.ConfigurationElementCollection.OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)

O OnDeserializeUnrecognizedElement método pode ser substituído em MyCollection classe para criar uma instância de tipo específico.Em vez de usar o sem parâmetros CallCreateNewElement método, use um novo que receba XmlReader:

  1. Ler atributo type (garantir sua existência e validade).
  2. Crie um novo elemento do tipo especificado.
  3. Chamar internal virtual void AssociateContext( BaseConfigurationRecord configRecord ) método de System.Configuration.ConfigurationElement no elemento.
  4. Chamar internal void CallInit() método de System.Configuration.ConfigurationElement no elemento.
  5. Retorne o elemento preparado.

Aliás, se não houver muitos elementos de coleção diferentes, considere usar algo como:

<myType1Collection>
    <add Type1SpecificProp="1" />
</myType1Collection>
<myType2Collection>
    <add Type2SpecificProp="2" />
</myType2Collection>

Dessa forma, você pode evitar lançar os itens de MyCollection para um tipo específico.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top