ServiceKnownType无法按预期工作

时间:2012-09-08 18:09:32

标签: c# .net wcf serialization datacontract

我有一个服务合约界面,如下所示:

[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'包含来自a的数据   映射到名称的类型   '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);
                }

这会创建一个ClientBase<IService>后代,它也会实现IService。接口实现(通过BuildInterfaceMethods构建)通过ClientBase提供的受保护的Channel属性路由每个方法调用。发出的类相当于:

    public sealed class ModelRepositoryProxy : ClientBase<IModelRepository>, IModelRepository
    {
        //Constructor omitted for brevity...
        ModelDescriptor IModelRepository.GetDescriptor(ModelHandle model)
        {
            return Channel.GetDescriptor(model);
        }
    }

我最终使用了this answer中概述的解决方案,在构建它之后不久,通过在客户端上为每个已知类型的服务调用IDesign AddGenericResolver。这将注册一个知道如何解析指定类型的DataContractSerializerOperationBehavior。我不清楚为什么必须允许客户端解析类型,但它可以工作。

1 个答案:

答案 0 :(得分:1)

看起来你在客户端有问题,而不是在服务器上。问题可能是因为您在生成客户端后添加了ServiceKnownType属性。