0

我有一个如下所示的服务合同接口:

[ServiceKnownType(typeof(GeometricModelDescriptor))]
[ServiceKnownType(typeof(TemplateModelDescriptor))]
[ServiceContract]
public interface IModelRepository
{
    [OperationContract]
    ModelDescriptor GetDescriptor(ModelHandle model);
}

它使用一些简单的数据合同类型,如下所示:

[DataContract]
public class ModelDescriptor
{
    //...
}

[DataContract]
public sealed class GeometricModelDescriptor : ModelDescriptor
{
    //...
}

当我尝试调用 GetDescriptor 方法时,我得到一个序列化异常,表明客户端代理无法反序列化该类型:

元素“ http://tempuri.org/:GetDescriptorResult ”包含映射到名称“MyNamespace:GeometricModelDescriptor”的类型的数据。反序列化器不知道映射到此名称的任何类型。考虑使用 DataContractResolver 或将与“GeometricModelDescriptor”对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到传递给 DataContractSerializer 的已知类型列表中。

我的理解是 ServiceKnownType 属性应该防止这种异常。我错过了什么?


人们要求提供客户端代码。这有点涉及,但这里是生成客户端代理包装器的逻辑的关键:

                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.

4

1 回答 1

1

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.

于 2012-09-08T18:20:43.247 回答