2009-06-10 17 views
8

Tengo un tipo, llamémoslo Data<TKey>. También tengo un contrato de servicio WCF que acepta un tipo (vamos a llamarlo Wrapper) con una propiedad de tipo Object (por razones que no entraré, esto no es opcional).¿Cómo especificar un tipo conocido de WCF en la configuración que es genérico?

[DataContract] 
public class Data<TKey> { ... } 

[DataContract] 
public class Wrapper 
{ 
    [DataMember] 
    public object DataItem { get; set; } 
} 

En este momento estoy enviando dos clases IntData y LongData:

[DataContract] 
public class IntData : Data<int> { /*empty*/ } 

[DataContract] 
public class LongData : Data<long> { /*empty*/ } 

Ambos están configurados en los tipos conocidos fichero de configuración. La configuración se asemeja a algo así:

<configuration> 
    <system.runtime.serialization> 
    <dataContractSerializer> 
     <declaredTypes> 
     <add type="Wrapper, TheirAssembly"> 
      <knownType type="IntData, MyAssembly"/> 
      <knownType type="LongData, MyAssembly"/> 
     </add> 
     </declaredTypes> 
    </dataContractSerializer> 
    </system.runtime.serialization> 
</configuration> 

En este punto, todo funciona bien.

Pero estoy a punto de agregar un tercer tipo y no me gusta tener las clases .NET vacías, innecesarias IntData y LongData. Solo existen porque ...

¡No sé cómo especificar tipos genéricos en la configuración de WCF!

Quiero hacer algo como esto, pero no conozco la sintaxis exacta.

<configuration> 
    <system.runtime.serialization> 
    <dataContractSerializer> 
     <declaredTypes> 
     <add type="Wrapper, TheirAssembly"> 
      <!-- this syntax is wrong --> 
      <knownType type="Data{System.Int32}, MyAssembly"/> 
      <knownType type="Data{System.Int64}, MyAssembly"/> 
     </add> 
     </declaredTypes> 
    </dataContractSerializer> 
    </system.runtime.serialization> 
</configuration> 

¿Cuál es la sintaxis correcta para esto?

(Tenga en cuenta también que no puedo poner [KnownType(...)] atributos en Wrapper, ya que no es mi tipo. Config parece ser la única manera.)

EDITAR

@ respuesta de baretta funcionó muy bien. Sin embargo, nótese que en un principio he recibido este error:

Type 'MyAssembly.Data`1[System.Int64]' cannot be added to list of known types since another type 'MyAssembly.Data`1[System.Int32]' with the same data contract name ' http://www.mycompany.com/MyAssembly:Data ' is already present.

que no lo mencionó en la pregunta original, pero mi tipo tiene un nombre de contrato de datos explícita. Algo como esto:

[DataContract(Name = "Data")] 
public class Data<TKey> { ... } 

Se ha producido el error anterior hasta que me quita el valor Name propiedad del atributo. Espero que también ayude a alguien más. No sé qué formato funciona en este escenario. Estos no:

[DataContract(Name = "Data\`1")] 
[DataContract(Name = "Data{TKey}")] 

¿Alguien sabe cómo hacer esto?

EDITAR 2

Gracias de nuevo a @baretta quien señaló que la sintaxis correcta es, de hecho:

[DataContract(Name = "Data{0}")] 
+0

Sí, lo sé! :) Edité mi respuesta – baretta

Respuesta

18

Un tipo genérico es crear instancias de una cadena, si la cadena sigue este patrón: nombre Clase seguido de un carácter "`", seguido por el número de parámetros de tipo (en este caso es 1), seguido de los parámetros de tipo incluidos dentro de "[]", y usando coma como separador de parámetro de tipo.

<configuration> 
    <system.runtime.serialization> 
    <dataContractSerializer> 
     <declaredTypes> 
     <add type="Wrapper, TheirAssembly"> 
      <!-- this syntax is all good --> 
      <knownType type="Data`1[System.Int32], MyAssembly"/> 
      <knownType type="Data`1[System.Int64], MyAssembly"/> 
     </add> 
     </declaredTypes> 
    </dataContractSerializer> 
    </system.runtime.serialization> 
</configuration> 

Editar: Podría añadir también, que si necesita ser especificado para los parámetros de tipo (althoug que no es el caso para la materia en mscorlib) información de ensamblado, a continuación, anidado "[]" se utiliza.

<knownType type="Data`1[[System.Int32, mscorlib]], MyAssembly"/> 

Editar: Puede personalizar los nombres de los tipos genéricos en los contratos de datos, utilizando el patrón de formato de cadena.

[DataContract(Name = "Data{0}")] 
public class Data<TKey> 
{...} 

Por defecto, el nombre generado para los datos < Int32> tipo es algo así como "DataOfInt32HJ67AK7Y", donde "HJ67AK7Y" es un hash generado a partir de la cadena "urn: por defecto", o el espacio de nombres de su clase, si tienes alguna. Pero "Datos {0}" le daría el nombre "DataInt32".

Más here. Eche un vistazo a la parte "Personalización de los nombres de los contratos de datos para los tipos genéricos".

+0

Gracias Baretta. Esta concisa respuesta funcionó perfectamente, una vez que resolví el problema con el atributo de nombre (ver mi edición). –

+0

Gracias por su edición. Esta es una gran respuesta y si pudiera votarla dos veces, lo haría :) –

+0

+1 para obtener información sobre cómo personalizar los nombres de DataContract. ¡Gracias! – Dennis

5

De here ...

Known types can also be defined in config as shown below.

<configuration> 
    <system.runtime.serialization> 
    <dataContractSerializer> 
     <declaredTypes> 
     <add type="MyCompany.Library.Shape`1, 
       MyAssembly, Version=2.0.0.0, Culture=neutral, 
       PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> 
      <knownType type="MyCompany.Library.Circle`1, 
         MyAssembly, Version=2.0.0.0, Culture=neutral, 
         PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> 
        <parameter index="0"/> 
      </knownType> 
     </add> 
     </declaredTypes> 
    </dataContractSerializer> 
    </system.runtime.serialization> 
</configuration> 

The above config specifies that the generic parameter for Circle is the same as the generic parameter for the declared type Shape. The config allows the definition of known type of arbitrary complexity. For example if it is needed to define Circle< Dictionary< string, T >> as the known type of Shape< T > (of course this is purely academic) it can be done as follows.

<configuration> 
    <system.runtime.serialization> 
    <dataContractSerializer> 
     <declaredTypes> 
     <add type="MyCompany.Library.Shape`1, 
       MyAssembly, Version=2.0.0.0, Culture=neutral, 
       PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> 
      <knownType type="MyCompany.Library.Circle`1, 
         MyAssembly, Version=2.0.0.0, Culture=neutral, 
         PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> 
        <parameter type="System.Collections.Generic.Dictionary`2"> 
         <parameter type="System.String"/> 
         <parameter index="0"/> 
        </parameter>     
      </knownType> 
     </add> 
     </declaredTypes> 
    </dataContractSerializer> 
    </system.runtime.serialization> 
</configuration> 

Note the use config element “parameter” with the attributes ‘type’ and ‘index’.

Cuestiones relacionadas