我有一个独特的客户端 - 服务器配置,它提出了一个有趣的问题。底线问题是:
如何为WCF服务序列化绑定和端点配置?
服务器
服务器由一个"云" WCF服务(称为Alice和Bob)在他们自己的应用程序域中运行,但在不同的机器上可能不是必需的。根据网络约束和访问权限,每个服务可能或可能不能与云中的其他服务通信。换句话说,任何特定服务都不知道它可以与哪些其他服务通信。这意味着任何app.configs都没有连接信息。
客户
客户端只能通过WCF与一个服务Alice进行通信。但是,客户端需要与云中的其他服务进行通信。这是通过让Alice将来自客户端的请求转发给他们的预期接收者来实现的(实际上是复数,但是当前示例只有Bob)。客户完全了解哪些服务能够连接到哪些其他服务,包括绑定和地址信息。
目标
现在假设客户端向Alice发送了一个旨在传递给Bob的请求。 Alice分析该请求并确定该消息需要以某种方式转发给Bob。 Alice现在必须使用适当的绑定和端点为Bob构建一个通道。如果Alice有一个包含所有信息的app.config,这不会有问题。但是,如上所述,app.config中没有此类信息。相反,Alice必须使用原始请求中包含的信息来构建频道。
尝试失败
我首先天真地将Binding
和EndpointAddress
属性添加到我的数据合同中:
[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; }
}
然后客户端调用Alice的WCF方法,如下所示:
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);
}
}
然而,这导致了对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:
不幸的是,添加KnownType
属性并不起作用(前6次尝试),因为出现了无穷无尽的异常串,跨越了许多很多类型。此外,这甚至不会考虑所有可能的绑定,因为任何人都可以使用我没有以这种方式明确列出的类型创建自定义绑定。
此时,我意识到用于在app.config文件中生成绑定和地址信息的XML实际上非常简单。看起来我应该做的就是以某种方式从配置类中退回。任何人都可以告诉我,我应该在一个相对简单的序列化问题中缺少什么?
答案 0 :(得分:0)
首先,我不认为您可以序列化Binding
和EndpointAddress
类。 (您可能不知道所有knownTypes,它是一个框架类)
您可以尝试将必要信息作为string
和Bob side
传递,解析字符串并创建Binding
和EndpointAddress
的实例以连接到其他services
}。