Como especificar um WCF tipo conhecido na configuração que é genérico?
-
13-09-2019 - |
Pergunta
Eu tenho um tipo, vamos chamá-lo Data<TKey>
. Eu também tenho um contrato de serviço WCF que aceita um tipo (vamos chamá-lo Wrapper
) com uma propriedade do tipo Object
(por razões que eu não vou entrar, isso não é opcional).
[DataContract]
public class Data<TKey> { ... }
[DataContract]
public class Wrapper
{
[DataMember]
public object DataItem { get; set; }
}
Agora estou enviando duas classes IntData
e LongData
:
[DataContract]
public class IntData : Data<int> { /*empty*/ }
[DataContract]
public class LongData : Data<long> { /*empty*/ }
Eles estão ambos configurados nos tipos conhecidos config. A assemelha configuração algo como isto:
<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>
Neste ponto, tudo funciona bem.
Mas estou prestes a adicionar um terceiro tipo e eu não gosto de ter o desnecessário, vazio classes de .NET IntData
e LongData
. Eles só existem porque ...
Eu não sei como especificar tipos genéricos na configuração WCF!
Eu quero fazer algo assim, mas não sei a sintaxe exata.
<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>
O que é a sintaxe correta para isso?
(Nota também que eu não posso colocar [KnownType(...)]
atributos de Wrapper
como não é meu tipo. Config parece ser o único caminho.)
Editar
resposta@ do baretta funcionou muito bem. Note, porém, que, inicialmente, eu recebi este erro:
Type 'MyAssembly.Data`1 [System.Int64]' não pode ser adicionada à lista de tipos conhecidos desde outro tipo 'MyAssembly.Data`1 [System.Int32]' com o mesmo nome contrato de dados ' http://www.mycompany.com/MyAssembly:Data ' já está presente.
Eu não mencionou isso na pergunta original, mas meu tipo tem um nome de contrato de dados explícito. Algo parecido com isto:
[DataContract(Name = "Data")]
public class Data<TKey> { ... }
O erro ocorreu acima até que eu removi o valor da propriedade Name
do atributo. Espero que ajude alguém com muito. Eu não sei o formato funciona neste cenário. Estes não fez:
[DataContract(Name = "Data\`1")]
[DataContract(Name = "Data{TKey}")]
Alguém sabe como fazer isso?
EDIT 2
Obrigado novamente para @baretta que apontaram que a sintaxe correta é de fato:
[DataContract(Name = "Data{0}")]
Solução
Um tipo genérico é instanciável de uma string, se a cadeia segue esse padrão: nome da classe seguido por um caractere "`", seguido do número de parâmetros de tipo (neste caso é 1), seguido pelos parâmetros de tipo fechado dentro "[]", e usando vírgula como tipo de parâmetro separador .
<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>
Edit:. Eu poderia também acrescentar que se as necessidades de informação de montagem deve ser especificado para os parâmetros de tipo (althoug não é o caso para o material em mscorlib), então aninhados "[]" é usado
<knownType type="Data`1[[System.Int32, mscorlib]], MyAssembly"/>
Edit:. Você pode personalizar nomes de tipos genéricos em contratos de dados, usando o padrão de formato de cadeia
[DataContract(Name = "Data{0}")]
public class Data<TKey>
{...}
Por padrão, o nome gerado para a dados
aqui . Ter um olhar para os "Personalização de dados Nomes contrato para tipos genéricos" parte de baixo da página.
Outras dicas
A partir aqui ...
Conhecido tipos também pode ser definida como na configuração mostrado abaixo.
<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>
Os especifica configuração acima que o parâmetro genérico para Circle é o mesmo que o parâmetro genérico para o tipo declarado Shape. A configuração permite a definição de tipo conhecido de complexidade arbitrária. Por exemplo, se ela é necessária para definir Círculo
> como o tipo conhecido de Forma (é claro que isso é puramente acadêmico) que pode ser feito como segue.
<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>
Observe o elemento de utilização de configuração “Parâmetro” com ‘tipo’ os atributos e ‘index’.