gpt4 book ai didi

wcf - 将 WCF 服务中的派生类型反序列化为基类型,但保留类型信息

转载 作者:行者123 更新时间:2023-12-04 04:31:01 24 4
gpt4 key购买 nike

我希望我的服务能够接受和返回派生自 BaseType 的类型实际上不知道这些类型将是什么。我几乎得到了使用自定义 DataContractResolver 的解决方案基于SharedTypeResolver from this excellent blog post .

难题中缺少的部分是我的服务将处理的类型可能不会被服务共享和知道,但我仍然想接受它们并知道应该是什么类型。我提出了以下充当堆栈的服务示例。您可以推送和弹出从 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();
}
}

这显然是我遇到的问题的一个大大简化的例子。客户端可以非常愉快地推送和弹出 BaseTypeDerivedType因为客户端和服务器都知道它们,但是如果客户端推送 UnsharedType如您所料,服务不知道我收到错误。

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:item. The InnerException message was 'Error in line 1 position 316. Element 'http://tempuri.org/:item' contains data from a type that maps to the name 'TestWcfClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null:TestWcfClient.UnsharedType'. The deserializer has no knowledge of any type that maps to this name. Consider changing the implementation of the ResolveName method on your DataContractResolver to return a non-null value for name 'TestWcfClient.UnsharedType' and namespace 'TestWcfClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.'. Please see InnerException for more details.



我目前的想法是添加 IExtensibleDataObjectBaseType保存非共享类型的值并使非共享类型看起来像 BaseType推送项目时的反序列化服务;弹出项目时需要发生相反的情况。我只是不知道该怎么做。到目前为止,我对可能方法的想法:
  • 进一步定制 DataContractResolver这可能涉及 TypeDelegator
  • 使用 IDataContractSurrogate代替非共享类型
  • 以某种方式保留推送项目时服务收到的序列化 XML,然后在弹出项目时在回复中使用它
  • 使用消息检查器来处理消息

  • 我不知道这些是否可行,会涉及什么或什么是最佳解决方案。你?

    最佳答案

    我使用消息检查器和实现 IExtensibleDataObject 的占位符类型在这方面取得了一些进展。 .检查器处理传入消息并将类型提示更改为占位符的提示,并将原始类型添​​加为属性。然后在回复中发送类型时,会发生相反的情况,从而使占位符看起来像原始类型。

    我对这个解决方案的不满在于它与服务绑定(bind),因为我必须包含服务的 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)
    {
    }
    }

    关于wcf - 将 WCF 服务中的派生类型反序列化为基类型,但保留类型信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15902437/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com