简体   繁体   English

ServiceKnownType无法按预期工作

[英]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: 当我尝试调用GetDescriptor方法时,出现一个序列化异常,指示客户端代理无法反序列化类型:

Element ' http://tempuri.org/:GetDescriptorResult ' contains data from a type that maps to the name 'MyNamespace:GeometricModelDescriptor'. 元素' http://tempuri.org/:GetDescriptorResult '包含来自映射到名称'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. 考虑使用DataContractResolver或将与“ GeometricModelDescriptor”相对应的类型添加到已知类型的列表中-例如,通过使用KnownTypeAttribute属性或将其添加到传递给DataContractSerializer的已知类型的列表中。

My understanding is that the ServiceKnownType attributes should prevent this exception. 我的理解是ServiceKnownType属性应防止出现此异常。 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 . 这将创建ClientBase<IService>子代,该IService也实现IService The interface implementation (built via BuildInterfaceMethods) routes each method call through the protected Channel property provided by ClientBase. 接口实现(通过BuildInterfaceMethods构建)通过ClientBase提供的受保护的Channel属性路由每个方法调用。 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. 我最终在构造此答案后不久,通过在客户端上为每种已知服务类型调用AddGenericResolver来使用此答案中概述的解决方案。 This registers a DataContractSerializerOperationBehavior that knows how to resolve the specified types. 这将注册一个知道如何解析指定类型的DataContractSerializerOperationBehavior。 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. 问题可能是由于生成客户端后添加了ServiceKnownType属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM