I have a type, let's call it Data<TKey>
. I also have a WCF service contract that accepts a type (lets call it Wrapper
) with a property of type Object
(for reasons I won't go into, this isn't optional).
[DataContract]
public class Data<TKey> { ... }
[DataContract]
public class Wrapper
{
[DataMember]
public object DataItem { get; set; }
}
Right now I'm sending two classes IntData
and LongData
:
[DataContract]
public class IntData : Data<int> { /*empty*/ }
[DataContract]
public class LongData : Data<long> { /*empty*/ }
They're both configured in the known types config file. The config resembles something like this:
<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>
At this point, everything works fine.
But I'm about to add a third type and I don't like having the unnecessary, empty .NET classes IntData
and LongData
. They only exist because...
I don't know how to specify generic types in WCF configuration!
I want to do something like this, but don't know the exact syntax.
<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>
What is the correct syntax for this?
(Note too that I cannot put [KnownType(...)]
attributes on Wrapper
as it's not my type. Config seems to be the only way.)
EDIT
@baretta's answer worked nicely. Note however that initially I received this 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.
I didn't mention it in the original question, but my type has an explicit data contract name. Something like this:
[DataContract(Name = "Data")]
public class Data<TKey> { ... }
The above error occurred until I removed the Name
property value from the attribute. Hope that helps someone else out too. I don't know what format works in this scenario. These didn't:
[DataContract(Name = "Data\`1")]
[DataContract(Name = "Data{TKey}")]
Anyone know how to do this?
EDIT 2
Thanks again to @baretta who pointed out that the correct syntax is in fact:
[DataContract(Name = "Data{0}")]
A generic type is instantiable from a string, if the string follows this pattern: Class name followed by a "`" character, followed by the number of type parameters(in this case it's 1), followed by the type parameters enclosed within "[]", and using comma as type parameter separator.
<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: I might also add, that if assembly information needs to be specified for the type parameters(althoug it's not the case for stuff in mscorlib), then nested "[]" is used.
<knownType type="Data`1[[System.Int32, mscorlib]], MyAssembly"/>
Edit: You can customize names of generic types in data contracts, using the string format pattern.
[DataContract(Name = "Data{0}")]
public class Data<TKey>
{...}
By default, the name generated for the Data<Int32> type is something like "DataOfInt32HJ67AK7Y", where "HJ67AK7Y" is a hash generated from the string "urn:default", or the namespace of your class, if you have any. But "Data{0}" would give it the name "DataInt32".
More here. Have a look at the "Customizing Data Contract Names for Generic Types" part down the page.