I have a service contract interface that looks like this:
[ServiceKnownType(typeof(GeometricModelDescriptor))]
[ServiceKnownType(typeof(TemplateModelDescriptor))]
[ServiceContract]
public interface IModelRepository
{
[OperationContract]
ModelDescriptor GetDescriptor(ModelHandle model);
}
It uses some simple data-contract types like this:
[DataContract]
public class ModelDescriptor
{
//...
}
[DataContract]
public sealed class GeometricModelDescriptor : ModelDescriptor
{
//...
}
When I attempt to call the GetDescriptor method, I get a serialization exception indicating that the client proxy can't deserialize the type:
Element 'http://tempuri.org/:GetDescriptorResult' contains data from a type that maps to the name 'MyNamespace:GeometricModelDescriptor'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'GeometricModelDescriptor' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.
My understanding is that the ServiceKnownType attributes should prevent this exception. What am I missing?
Folks have asked for the client code. It's a bit involved, but here is the crux of the logic that generates the client proxy wrapper:
var baseType = typeof(ClientBase<>).MakeGenericType(typeof(TService));
var proxyType = _module.DefineType("ProxyType" + typeof(TService).Name,
TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public,
baseType,
new[] { typeof(TService) });
var constructor = proxyType.DefineConstructor(MethodAttributes.Public,
CallingConventions.HasThis,
new[] { typeof(ServiceEndpoint)});
var il = constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, baseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
null,
new[] {typeof(ServiceEndpoint)},
null));
il.Emit(OpCodes.Ret);
var interfaces = FlattenInterfaces(typeof(TService)).ToList();
foreach (var interfaceType in interfaces)
{
BuildInterfaceMethods(typeof(TService), proxyType, interfaceType);
}
This creates a ClientBase<IService>
descendent, which also implements IService
. The interface implementation (built via BuildInterfaceMethods) routes each method call through the protected Channel property provided by ClientBase. The emitted class is the equivalent of this:
public sealed class ModelRepositoryProxy : ClientBase<IModelRepository>, IModelRepository
{
//Constructor omitted for brevity...
ModelDescriptor IModelRepository.GetDescriptor(ModelHandle model)
{
return Channel.GetDescriptor(model);
}
}
I ended up using a solution outlined in this answer, by calling the IDesign AddGenericResolver
on the client for each service known type shortly after constructing it. This registers a DataContractSerializerOperationBehavior that knows how to resolve the specified types. It's not clear to me why this is necessary to allow the client to resolve the types, but it works.
It looks like you have problem on the client side, not on the server. The problem might be because of you have added ServiceKnownType
attributes after you have generated the client.