0

Edit: consider using the information from this post.

I have a unique client-server configuration that is presenting an interesting problem. The bottom-line question is:

How can I serialize binding and endpoint configurations for a WCF service?

The Server
The server consists of a "cloud" of WCF services (call them Alice and Bob) running in their own app domains possibly but not necessary on different machines. Each service may or may not be able to communicate with another service in the cloud depending on networking constraints and access privileges. In other words, any particular service will know nothing about which other services it can communicate with. That means no connection info in any of the app.configs.

The Client
The client is only able to communicate with exactly one service, Alice, via WCF. However, the client needs to communicate with other services in the cloud. This is accomplished by having Alice forward requests from the client to their intended recipients (plural in reality, but just Bob for the current example). The client has full knowledge of which services are able to connect to which other services, including binding and address information.

The Objective
Now suppose the client sends a request to Alice that is intended to be delivered to Bob. Alice analyzes the request and determines that the message needs to be forwarded to Bob somehow. Alice must now construct a channel to Bob using an appropriate binding and endpoint. If Alice had an app.config with all the info, this would not be a problem. However, as I described above, no such information is available in the app.config. Instead, Alice must use information contained in the original request to construct the channel.

Failed Attempts
I first naively added Binding and EndpointAddress properties to my data contract:

[DataContract]
public class ConnectivityGraph
{
    [DataMember]
    public string FromNode = "Alice";

    [DataMember]
    public string ToNode = "Bob";

    [DataMember]
    public Binding BobsBinding { get; set; }

    [DataMember]
    public EndpointAddress BobsEndpoint { get; set; }
}

Then the client calls Alice's WCF method like this:

private void btnToBobViaAlice_Click(object sender, EventArgs e)
{
    try
    {
        var alice = new HyperNode("Alice");

        // Build the node graph

        var msg = new HyperNodeMessage("HyperNodeTestClient")
        {
            CommandName = "ValidCommand",
            IntendedRecipientNodes = new List<string>
            {
                "Bob"
            },
            HyperNodeConnectivityGraph = new HyperNodeConnectivityGraph
            {
                BobsBinding = new NetHttpBinding(),
                BobsEndpoint = new EndpointAddress("net.tcp://localhost:8001/HyperNode/Bob")
            }
        };

        var result = alice.ProcessMessage(msg);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

However, this resulted in an ungodly exception on the call to alice.ProcessMessage(msg):

System.ServiceModel.CommunicationException was caught
  HResult=-2146233087
  Message=There was an error while trying to serialize parameter http://tempuri.org/:message. The InnerException message was 'Type 'System.ServiceModel.NetHttpBinding' with data contract name 'NetHttpBinding:http://schemas.datacontract.org/2004/07/System.ServiceModel' 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.
  Source=mscorlib
  StackTrace:
    Server stack trace: 
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameter(XmlDictionaryWriter writer, PartInfo part, Object graph)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameters(XmlDictionaryWriter writer, PartInfo[] parts, Object[] parameters)
       at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)
       at System.ServiceModel.Dispatcher.OperationFormatter.SerializeBodyContents(XmlDictionaryWriter writer, MessageVersion version, Object[] parameters, Object returnValue, Boolean isRequest)
       at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota)
       at System.ServiceModel.Channels.BinaryMessageEncoderFactory.BinaryMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
       at System.ServiceModel.Channels.FramingDuplexSessionChannel.EncodeMessage(Message message)
       at System.ServiceModel.Channels.FramingDuplexSessionChannel.OnSendCore(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.TransportDuplexSessionChannel.OnSend(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.OutputChannel.Send(Message message, TimeSpan timeout)
       at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
    Exception rethrown at [0]: 
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at Hyper.Applications.NodeContracts.IHyperNodeService.ProcessMessage(HyperNodeMessage message)
       at Hyper.Applications.NodeProxies.HyperNode.ProcessMessage(HyperNodeMessage message) in c:\GitRoot\Hyper\Hyper.Applications.NodeProxies\HyperNode.cs:line 28
       at HyperNodeTestClient.MainForm.btnToBobViaAlice_Click(Object sender, EventArgs e) in c:\GitRoot\Hyper\HyperNodeTestClient\MainForm.cs:line 72
  InnerException: System.Runtime.Serialization.SerializationException
       HResult=-2146233076
       Message=Type 'System.ServiceModel.NetHttpBinding' with data contract name 'NetHttpBinding:http://schemas.datacontract.org/2004/07/System.ServiceModel' 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.
       Source=System.Runtime.Serialization
       StackTrace:
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
            at WriteHyperNodeConnectivityGraphToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
            at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
            at WriteHyperNodeMessageToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract )
            at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
            at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.SerializeParameterPart(XmlDictionaryWriter writer, PartInfo part, Object graph)
       InnerException: 

Unfortunately, adding KnownType attributes didn't work (for the first 6 attempts) because there arose an endless string of exceptions spanning many, many, many types. Furthermore, this wouldn't even account for all the possible bindings since anyone can create a custom binding using types that I have not explicitly listed in this way.

At this point, I realized that the XML for generating binding and address info in the app.config file is actually pretty simple. It seems like all I should have to do is piggy back off of the Configuration class somehow. Can anyone tell me what I'm missing in what should be a relatively simple serialization problem?

Community
  • 1
  • 1
Josh Heitz
  • 68
  • 8

1 Answers1

0

First of all , I don't think you can serialize Binding and EndpointAddress classes. (You may not know all the knownTypes, it being a framework class)

You might try passing necessary information as string and at Bob side, parse the string and create the instances of Binding and EndpointAddress to connect to other services.

ANewGuyInTown
  • 5,957
  • 5
  • 33
  • 45
  • This is not an answer, just a restatement of my problem. I know that the framework is somehow able to take the XML from the app config and turn it into meaningful objects. I want to duplicate that process programmatically without having to reinvent that very complex wheel. – Josh Heitz Sep 10 '15 at 17:02
  • What I meant was why don't you try passing the Binding and EnpointAddress as plain text and construct the strongly typed objects at the bob end. You could use the channel factory if both your client and server are .net and if could share the same contract. – ANewGuyInTown Sep 11 '15 at 00:54