【发布时间】:2015-02-21 21:33:37
【问题描述】:
编辑:考虑使用来自this post的信息。
我有一个独特的客户端-服务器配置,它提出了一个有趣的问题。底线问题是:
如何序列化 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 实际上非常简单。似乎我所要做的就是以某种方式背离配置类。谁能告诉我在应该是一个相对简单的序列化问题中缺少什么?
【问题讨论】:
-
认真的人吗?连一条评论都没有?至少有人可以帮我把这个问题做得更好吗?
-
这些项目是否必须标记为 [Serializable] 才能正常工作?
标签: c# web-services wcf serialization