bat*_*wad 9 wcf serialization types datacontractserializer
我希望我的服务能够接受并返回派生的类型,BaseType而不会真正知道这些类型是什么.我几乎得到了一个解决方案,使用DataContractResolver基于此优秀博客文章的SharedTypeResolver的自定义.
这个难题的缺失部分是我的服务将处理的类型可能不会被服务共享和知道,但我仍然想接受它们并且知道该类型应该是什么.我已经提出了以下作为堆栈的服务示例.您可以推送和弹出从BaseType您提供的任何类型派生SharedTypeResolver的类型,并在客户端和服务器之间共享类型.
[DataContract]
public class BaseType
{
[DataMember]
public string SomeText { get; set; }
public override string ToString()
{
return this.GetType().Name + ": " + this.SomeText;
}
}
[DataContract]
public class DerivedType : BaseType
{
[DataMember]
public int SomeNumber { get; set; }
public override string ToString()
{
return base.ToString() + ", " + this.SomeNumber;
}
}
[ServiceContract]
public interface ITypeStack
{
[OperationContract]
void Push(BaseType item);
[OperationContract]
BaseType Pop();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TypeStackService : ITypeStack
{
private Stack<BaseType> stack = new Stack<BaseType>();
public void Push(BaseType item)
{
this.stack.Push(item);
}
public BaseType Pop()
{
return this.stack.Pop();
}
}
Run Code Online (Sandbox Code Playgroud)
这显然是我遇到的问题的简化示例.客户端可以非常快乐地推送和弹出,BaseType或者DerivedType因为客户端和服务器都知道它们,但是如果客户端推送UnsharedType服务不知道哪个,我会得到您所期望的错误.
格式化程序在尝试反序列化消息时抛出异常:尝试反序列化参数http://tempuri.org/:item时出错 .InnerException消息是'第1行中的错误位置316.元素' http://tempuri.org/:item '包含映射到名称'TestWcfClient,Version = 1.0.0.0,Culture = neutral,PublicKeyToken =的类型的数据空:TestWcfClient.UnsharedType".反序列化器不知道映射到此名称的任何类型.考虑更改DataContractResolver上的ResolveName方法的实现,以返回名称'TestWcfClient.UnsharedType'和命名空间'TestWcfClient,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'.'的非空值.有关更多详细信息,请参阅InnerException.
我目前的想法是添加IExtensibleDataObjectto BaseType来保存非共享类型的值,并BaseType在推送项目时使非共享类型看起来像反序列化的服务; 当弹出一个项目时,需要进行相反的操作.我只是不确定如何去做.到目前为止我对可能的方法的看法:
DataContractResolver可能涉及到TypeDelegatorIDataContractSurrogate代替非共享类型我不知道这些是否有效,涉及的内容或最佳解决方案是什么.你呢?
我使用消息检查器和实现IExtensibleDataObject. 检查器操作传入的消息并将类型提示更改为占位符的类型提示,并将原始类型添加为属性。当该类型在回复中发送出去时,会发生相反的情况,从而使占位符看起来像原始类型。
我对该解决方案的不满在于它绑定到服务,因为我必须包含服务的 XML 命名空间并显式命名要操作的方法和参数。除此之外,它似乎工作得相当好,尽管我只在从 派生的相当简单的类型上测试了它BaseType。
有人可以对此进行改进吗?里面有一份赏金给你。
public class PlaceholderType : BaseType, IExtensibleDataObject
{
[IgnoreDataMember]
public string OriginalTypeName { get; set; }
[IgnoreDataMember]
public string OriginalNamespace { get; set; }
ExtensionDataObject IExtensibleDataObject.ExtensionData { get; set; }
}
public class FunkadelicInspector : IDispatchMessageInspector, IContractBehavior
{
const string PlaceholderNamespace = "http://my.placeholder.namespace";
const string ServiceNamespace = "http://tempuri.org/";
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
XmlDocument xmlDoc = ReadMessage(request);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
// Dislike: having to know the service namespace, method and parameters
nsmgr.AddNamespace("s", ServiceNamespace);
XmlNode itemElement = xmlDoc.SelectSingleNode("//s:Push/s:item", nsmgr);
if (itemElement != null)
{
XmlAttribute typeAttribute = itemElement.Attributes["type", "http://www.w3.org/2001/XMLSchema-instance"];
if (typeAttribute != null)
{
// Record original type
string[] parts = typeAttribute.Value.Split(':');
string originalTypeName = parts[1];
// Replace with placeholder type
typeAttribute.Value = parts[0] + ":" + typeof(PlaceholderType).FullName;
// Record original assembly
XmlAttribute nsAtt = itemElement.Attributes["xmlns:" + parts[0]];
string originalAssembly = nsAtt.Value;
// Replace with placeholder type's assembly
nsAtt.Value = typeof(PlaceholderType).Assembly.FullName;
// Add placeholders
itemElement.AppendChild(xmlDoc.CreateElement("OriginalType", PlaceholderNamespace)).InnerText = originalTypeName;
itemElement.AppendChild(xmlDoc.CreateElement("OriginalAssembly", PlaceholderNamespace)).InnerText = originalAssembly;
}
}
//Now recreate the message
request = WriteMessage(request, xmlDoc);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
XmlDocument xmlDoc = ReadMessage(reply);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("s", ServiceNamespace);
nsmgr.AddNamespace("plc", PlaceholderNamespace);
// Dislike: having to know the service namespace, method and parameters
XmlNode resultElement = xmlDoc.SelectSingleNode("//s:PopResponse/s:PopResult", nsmgr);
if (resultElement != null)
{
XmlElement originalType = resultElement.SelectSingleNode("plc:OriginalType", nsmgr) as XmlElement;
XmlElement originalAssembly = resultElement.SelectSingleNode("plc:OriginalAssembly", nsmgr) as XmlElement;
if (originalType != null && originalAssembly != null)
{
// Replace original type
XmlAttribute type = resultElement.Attributes["type", "http://www.w3.org/2001/XMLSchema-instance"];
string[] parts = type.Value.Split(':'); // 0 is an alias for the assembly, 1 is the type
type.Value = parts[0] + ":" + originalType.InnerText;
// Replace original assembly
XmlAttribute ass = resultElement.Attributes["xmlns:" + parts[0]];
ass.Value = originalAssembly.InnerText;
// Remove placeholders
resultElement.RemoveChild(originalType);
resultElement.RemoveChild(originalAssembly);
}
}
//Now recreate the message
reply = WriteMessage(reply, xmlDoc);
}
private static Message WriteMessage(Message original, XmlDocument xmlDoc)
{
MemoryStream ms = new MemoryStream();
xmlDoc.Save(ms);
ms.Position = 0;
XmlReader reader = XmlReader.Create(ms);
Message newMessage = Message.CreateMessage(reader, int.MaxValue, original.Version);
newMessage.Properties.CopyProperties(original.Properties);
return newMessage;
}
private static XmlDocument ReadMessage(Message message)
{
MemoryStream ms = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(ms))
{
message.WriteMessage(writer); // the message was consumed here
writer.Flush();
}
ms.Position = 0;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms);
return xmlDoc;
}
void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(this);
}
void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1030 次 |
| 最近记录: |