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?