我在基于WCF的Web服务中有以下合同:
public List<string> GetAllPossibleQueryEngineHostNames(Instance instance);
我从客户端这样称呼它:
string instance = "value";
svc.GetAllPossibleQueryEngineHostNames(instance);
注意instance
是String
,而不是Instance
对象。我得到了例外:
格式化程序在尝试反序列化时抛出异常 消息:尝试反序列化参数时出错 http://tempuri.org/:instance。 InnerException消息是'错误输入 第1行位置152.期待状态'元素'..遇到'文本' 名称为'',名称空间''。 ”。有关更多信息,请参阅InnerException 的信息。
我正在寻找一种方法来解决此问题,而无需更改任何客户端代码,因为我们需要能够支持连接到此服务的旧版客户端。< / p>
注意,客户端和服务器上的Instance
类都有一个隐式转换运算符:
public static implicit operator Instance(string value)
{
// Converts string to an Instance
}
但是,在反序列化时,WCF似乎没有考虑到这一点。有没有办法控制WCF如何反序列化Instance
个实例,允许它是字符串或Instance
对象?
答案 0 :(得分:2)
标准DataContractSerializer不会自动将字符串转换为实例类型。你不能在WCF中重载该方法,所以我建议你只需将字符串作为参数然后对实例进行转换。
public List<string> GetAllPossibleQueryEngineHostNames(string instanceName)
{
var instance = Instance(instanceName);
// Do everything else
}
我会考虑保持这种方式或添加另一个实际接受实例参数的方法。
最后一个选项是将byte []作为参数,并尝试将其解析为您需要的类型,并进行检查以确定它是字符串还是类型。
答案 1 :(得分:0)
有这个工作。这是我的代码,以防任何人对解决方案感兴趣。基本上,我使用以下类创建了一个名为Legacy.cs的新文件:
public class InstanceSerializer : XmlObjectSerializer
{
const string localName = "instance";
public override bool IsStartObject(XmlDictionaryReader reader)
{
return String.Equals(reader.LocalName, localName, StringComparison.OrdinalIgnoreCase);
}
public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
{
string xml = reader.ReadOuterXml();
XDocument doc = XDocument.Parse(xml);
string shortCode = doc.Descendants()
.Where(e => e.Name.LocalName == "ShortCode")
.Select(e => e.Value)
.FirstOrDefault();
string connStr = doc.Descendants()
.Where(e => e.Name.LocalName == "ConnectionString")
.Select(e => e.Value)
.FirstOrDefault();
if (connStr != null || shortCode != null) // Instance passed as Instance object
{
return new Instance(shortCode, connStr);
}
// Instance passed as String
Instance instance = ((XElement) doc.FirstNode).Value;
return instance;
}
public override void WriteEndObject(XmlDictionaryWriter writer)
{
writer.WriteEndElement();
}
public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
{
}
public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
{
writer.WriteStartElement(localName);
}
}
public class InstanceBehavior : DataContractSerializerOperationBehavior
{
public InstanceBehavior(OperationDescription operation) : base(operation) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return typeof(Instance) == type
? new InstanceSerializer()
: base.CreateSerializer(type, name, ns, knownTypes);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return typeof(Instance) == type
? new InstanceSerializer()
: base.CreateSerializer(type, name, ns, knownTypes);
}
}
public class SupportStringInstanceAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
ReplaceSerializerOperationBehavior(contractDescription);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
ReplaceSerializerOperationBehavior(contractDescription);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
private static void ReplaceSerializerOperationBehavior(ContractDescription contract)
{
foreach (OperationDescription od in contract.Operations)
{
for (int i = 0; i < od.Behaviors.Count; i++)
{
DataContractSerializerOperationBehavior dcsob = od.Behaviors[i] as DataContractSerializerOperationBehavior;
if (dcsob != null)
{
od.Behaviors[i] = new InstanceBehavior(od);
}
}
}
}
}
最后,我将[SupportStringInstance]
添加到我的服务实现的顶部(接口或类应该这样做)。
注意,这仅支持反序列化 Instance
对象(无论它是作为字符串还是对象传入)。您还必须实施InstanceSerializer.WriteObjectContent
以支持序列化,幸运的是,我不需要这样做(至少还有)。