9

¿Es posible tener un CollectionElementCollection con un número de diferentes por CollectionElements tipo, por ejemplo:ConfigurationElementCollection con un número de ConfigurationElements de diferente tipo

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

He todas las clases requeridas para tal solución:

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

pero cuando comienzo mi solicitud me estoy poniendo al lado de error predecible:

Unrecognized attribute 'Type1SpecificProp'.

porque Type1SpecificProp se define en MyType1 no MyElement, especialmente si MyCollection tiene método siguiente:

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

es decir rendimientos clase base así OnDeserializeUnrecognizedAttribute() en hijo clasificado nunca son sido llamados.

Entonces la pregunta es: ¿cómo dejar que las clases de niños resuelvan los elementos desconocidos por su cuenta?

Respuesta

2

Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T> de EntLib5 hacen este trabajo como un encanto.

+0

Sé que esta respuesta se publicó hace un tiempo, pero ¿podría entrar en detalles un poco más si está marcando esto como la respuesta aceptada? – goric

+0

@goric: debe heredarlo y anular 'RetrieveConfigurationElementType' para sustituir el tipo en tiempo de ejecución. Consulte también [NameTypeConfigurationElementCollection] (http://msdn.microsoft.com/en-us/library/ee939739%28PandP.50%29.aspx) – abatishchev

1

Se debe crear una instancia de tipo específico (MyType1 y MyType2) para que las clases secundarias puedan resolver elementos o atributos desconocidos por sí mismos.

Dado que el método CreateNewElement no proporciona ninguna información sobre los atributos de un elemento, ese no es el lugar donde puede producirse una instanciación de tipo específico.

Después de algo de investigación a través del reflector, se llega a siguiente pila de llamadas parcial:

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

El método OnDeserializeUnrecognizedElement se puede anular en MyCollection clase con el fin de crear instancia de tipo específico. En lugar de utilizar el método sin parámetros CallCreateNewElement, utilice uno nuevo que recibe XmlReader:

  1. leer el atributo type (asegurar su existencia y validez).
  2. Crear nuevo elemento del tipo especificado.
  3. Llamada internal virtual void AssociateContext(BaseConfigurationRecord configRecord) método de System.Configuration.ConfigurationElement en el elemento.
  4. Llamada internal void CallInit() método de System.Configuration.ConfigurationElement en el elemento.
  5. Devuelve el elemento preparado.

Por cierto, si no habrá demasiados elementos de colección diferentes, considere el uso de algo como:

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

esa manera se puede evitar la proyección de los elementos de MyCollection de tipo específico.

11

Miré esto también. PolymorphicConfigurationElementCollection<T>seems deprecated. Editar: no lo es, vea el comentario de 'abatishchev' a continuación, yo acababa de vincular una versión anterior.

La solución de Rest Wing era prometedora, pero desafortunadamente requería invocar métodos internos que residían en otro espacio de nombres. Si bien esto es posible a través de la reflexión, en este caso no recibirá precios para la belleza de la codificación.

me hicieron pozos en la fuente con la reflexión demasiado y se le ocurrió la siguiente solución:

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

La anulación de la CollectionType es REQUERIDO, incluso si esto se ha especificado a través del atributo en la parte superior. Cuando no se anula, la clase de base 'CollectionType' todavía se refiere a 'AddRemoveClearMap' que no activará la función 'CreateNewElement (string elementName)' requerida, pero su variante sin parámetros 'CreateNemElement()'. Por la misma razón, la función sobrescrita IsElementName debe devolver verdadera.

Tenga en cuenta que he creado una ElementBaseConfig que es la clase base de MyType1Config y MyType2Config en la que puede definir algunos atributos compartidos.

+2

Ha vinculado ver. 3.1, [aquí está el reciente] (http://msdn.microsoft.com/en-us/library/bb750549%28PandP.50%29.aspx) y no obsoleto – abatishchev

+0

Tiene razón, cambió mi respuesta para reflejar esto. Gracias por notarlo. – user1088858

Cuestiones relacionadas