You cannot define WCF contracts that rely on generic type
parameters. Generics are specific to .NET, and using them would violate
the service-oriented nature of WCF. However, you can use bounded generic
types in your data contracts, as long as you specify the type parameters
in the service contract and as long as the specified type parameters have
valid data contracts, as shown in Example 1.Example 1. Using bounded generic types
[DataContract] class MyClass<T> { [DataMember] public T MyMember; }
[ServiceContract] interface IMyContract { [OperationContract] void MyMethod(MyClass<int> obj); }
|
When you import the metadata of a data contract such as the
one in Example 3-16, the imported types
have all type parameters replaced with specific types, and the data
contract itself is renamed to this format:
<Original name>Of<Type parameter names><hash>
Using the same definitions as in Example 1, the imported data contract and
service contract will look like this:
[DataContract]
class MyClassOfint
{
int MyMemberField;
[DataMember]
public int MyMember
{
get
{
return MyMemberField;
}
set
{
MyMemberField = value;
}
}
}
[ServiceContract]
interface IMyContract
{
[OperationContract]
void MyMethod(MyClassOfint obj);
}
If, however, the service contract were to use a custom type such as
SomeClass instead of int:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClass<T>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass> obj);
the exported data contract might look like this:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClassOfSomeClassMTRdqN6P
{...}
[OperationContract(...)]
void MyMethod(MyClassOfSomeClassMTRdqN6P obj);
where MTRdqN6P is some
quasi-unique hash of the generic type parameter and the containing
namespace. Different data contracts and namespaces will generate different
hashes. The hash is in place to reduce the overall potential for a
conflict with another data contract that might use another type parameter
with the same name. No hash is created for the implicit data contracts of
the primitives when they are used as generic type parameters, since the
type int is a reserved word and the
definition of MyClassOfint is unique.
In most cases, the hash is a cumbersome over-precaution. You can
specify a different name for the exported data contract by simply
assigning it to the data contract’s Name property. For
example, given this service-side data contract:
[DataContract]
class SomeClass
{...}
[DataContract(Name = "MyClass")]
class MyClass<T>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass> obj);
the exported data contract will be:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClass
{...}
[OperationContract]
void MyMethod(MyClass obj);
However, by doing this, you run the risk of some ambiguity, since
two different custom generic types will result in the same type
name.
If you still want to combine the name of the generic type parameter
with that of the data contract, use the {<number>} directive, where the number is
the ordinal number of the type parameter. For example, given this
service-side definition:
[DataContract]
class SomeClass
{...}
[DataContract(Name = "MyClassOf{0}{1}")]
class MyClass<T,U>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass,int> obj);
the exported definition will be:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClassOfSomeClassint
{...}
[OperationContract(...)]
void MyMethod(MyClassOfSomeClassint obj);
Warning: The number of type parameters specified is not verified at compile
time. Any mismatch will yield a runtime exception.
Finally, you can append # after
the number to generate the unique hash. For example, given this data
contract definition:
[DataContract]
class SomeClass
{...}
[DataContract(Name = "MyClassOf{0}{#}")]
class MyClass<T>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass> obj);
the exported definition will be:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClassOfSomeClassMTRdqN6P
{...}
[OperationContract]
void MyMethod(MyClassOfSomeClassMTRdqN6P obj);