没有已知类型的WCF反序列化

时间:2014-03-31 14:35:36

标签: c# wcf serialization

首先,我希望我提供足够的信息以使事情变得清晰,如果没有请求更多。

我使用Duplex Channel和OneWay方法调用进行有效的WCF通信。 ServiceHost位于使用NetPipeBinding的托管WPF应用程序内,客户端位于该应用程序内的AppDomain中。只要所有类型都是原始类型(字符串,日期时间,...)或指定为已知类型(List<object>List<string>),一切都按预期工作。但是我需要发送其他类型,我不能添加已知的类型属性,因为我在编译时不知道它们。

正如我在这里阅读(http://msdn.microsoft.com/library/ms731923(v=vs.100).aspx)所有支持公共属性的公共类型都是受支持的,使用SerializableAttribute修饰的类型也是如此。

我试图转移一个非常简单的类:

public class ADT
{
  public string Name { get; set; }
}

并作为第二次尝试

[Serializable]
public class SerializableADT
{
  public string Name { get; set; }
}

并按照Herdo的建议

[DataContract]
public class DataContractADT
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public object Value { get; set; }
}

但所有三种类型的反序列化都失败了。

  

尝试序列化参数时出错   _http://tempuri.org/:的returnValue。 InnerException消息是'Type   带有数据协定名称的'TestLibraries.SeriablizableADT'   'SeriablizableADT:_http://schemas.datacontract.org/2004/07/TestLibraries'   不是预期的。考虑使用DataContractResolver或添加任何   静态地知道已知类型列表的类型 - 例如,   通过使用KnownTypeAttribute属性或将它们添加到   传递给DataContractSerializer的已知类型列表。'。请参阅   InnerException以获取更多详细信息。

如何在没有任何编译时间更改的情况下编组符合MSDN规则的任何类型(例如,使用Serializable修饰)?

2 个答案:

答案 0 :(得分:2)

如果这是一个完全内部的服务,您可以切换到使用NetDataContractSerializer,它通过包含完整的类型和汇编信息解决了这个问题(注意这个序列化器完全打破了互操作性和合同版本控制 - 所以永远不要将它用于外部公开的服务)。不需要KnownType。要使用它,您需要一个行为和一个属性。您可以在合同或单个操作中放置以下属性:

public class UseNetDataContractSerializerAttribute : Attribute, IOperationBehavior, IContractBehavior
{
    void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    void IOperationBehavior.Validate(OperationDescription description)
    {
    }

    void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }

    private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
    {
        var behavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();

        if (behavior != null)
        {
            description.Behaviors.Remove(behavior);
            description.Behaviors.Add(new NetDataContractSerializerOperationBehavior(description));
        }
    }

    void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        foreach (var operation in contractDescription.Operations)
        {
            ReplaceDataContractSerializerOperationBehavior(operation);
        }
    }

    void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        foreach (var operation in contractDescription.Operations)
        {
            ReplaceDataContractSerializerOperationBehavior(operation);
        }
    }

    void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }

    void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }
}

public class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
    public NetDataContractSerializerOperationBehavior(OperationDescription operation)
        : base(operation)
    {
    }

    public NetDataContractSerializerOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
        : base(operation, dataContractFormatAttribute)
    {
    }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        return new NetDataContractSerializer(name, ns);
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new NetDataContractSerializer(name, ns);
    }
}

答案 1 :(得分:0)

我也未能使用未知类型,因此我走了一条丑陋的路径:对于编译时未知的类型,我手动序列化它们并通过WCF传输字节流。

编辑:在查看DataContractResolver后,它看起来几乎相同: http://msdn.microsoft.com/de-de/library/dd807504(v=vs.110).aspx