ConfigurationElementCollection com vários ConfigurationElements de tipos diferentes
-
29-10-2019 - |
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?
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
:
- Ler atributo
type
(garantir sua existência e validade). - Crie um novo elemento do tipo especificado.
- Chamar
internal virtual void AssociateContext( BaseConfigurationRecord configRecord )
método deSystem.Configuration.ConfigurationElement
no elemento. - Chamar
internal void CallInit()
método deSystem.Configuration.ConfigurationElement
no elemento. - 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.