ServiceKnownType not working as expected

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.

Answers


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.


Need Your Help

WindowsRT Surface(Tablet) identifying

c# windows-runtime windows-store-apps

How do I uniquely identify a surface(tablet)? How do I get id of a tablet? there is a way using `HardwareIdentification.GetPackageSpecificToken(null). The problem is, it changes for simple hardware

asp.net session object being set to empty

c# asp.net session

I have a Session object in my asp.net app that I am setting to the userID value. The session object gets instantiated in the gloal.asax. The value is being set on a button_click event when a user s...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.