1
votes

first, I hope I provide enough information to make things clear, if not please ask for more.

I have a working WCF communication using Duplex Channel and OneWay method calls. The ServiceHost is located inside a managed WPF application using NetPipeBinding, the client lives in a AppDomain inside that application. Everything is working as expected as long as all types are primitive (string, DateTime, ...) or specified as known type (List<object>, List<string>). But I need to send other types, for which I can´t add a known type attribute because I don´t know them at compile time.

As I read here (http://msdn.microsoft.com/library/ms731923(v=vs.100).aspx) all public types with public properties are supported, and so are types decorated with SerializableAttribute.

I tried to transfer a very simple class:

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

and as a second try

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

and as suggested by Herdo

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

but the deserialization fails for all three types.

There was an error while trying to serialize parameter _http://tempuri.org/:returnValue. The InnerException message was 'Type 'TestLibraries.SeriablizableADT' with data contract name 'SeriablizableADT:_http://schemas.datacontract.org/2004/07/TestLibraries' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.

How can I marshal any type that meets the MSDN rules (e.g. decorated with Serializable) without any compile time changes?

2
Did you try the DataContract attribute for SerializeableADT?Herdo
Yes, I did - but I get the same error. I´ve read that Serializable and DataContract behaves the same as long as the default DataContractSerializer is used. I´m experimenting the DataContractResolver way (from exception message) at the moment - but still no luck.thomek

2 Answers

2
votes

If this is a completely internal service, you can switch to using the NetDataContractSerializer, which solves this problem by including full type and assembly information (note this serializer completely breaks interoperability and contract versioning - so never use it for externally-exposed services). No KnownType necessary. To use it, you need a behavior and an attribute. You can place the following attribute on your contract or on a single operation:

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);
    }
}
0
votes

I also failed in using unknown types so I went the ugly path: for types unknown at compile time I serialize them manually and transfer the byte stream via WCF.

Edit: After looking into the DataContractResolver, it looks pretty much the same: http://msdn.microsoft.com/de-de/library/dd807504(v=vs.110).aspx