2013-04-09 15 views
9

Tôi muốn dịch vụ của tôi để có thể chấp nhận và trả lại các loại có nguồn gốc từ BaseType mà không thực sự biết những gì các loại sẽ được. Tôi đã gần như có một giải pháp bằng cách sử dụng tùy chỉnh DataContractResolver dựa trên SharedTypeResolver from this excellent blog post.Deserialize các loại có nguồn gốc trong WCF dịch vụ như các loại cơ sở nhưng giữ lại thông tin loại

Phần còn thiếu của câu đố là các loại dịch vụ của tôi sẽ xử lý có thể không được chia sẻ và được biết đến với dịch vụ nhưng tôi vẫn muốn chấp nhận chúng và nhận thức được loại nên có. Tôi đã đưa ra ví dụ sau về một dịch vụ hoạt động như một chồng. Bạn có thể đẩy và bật bất kỳ loại nào có nguồn gốc từ BaseType miễn là bạn sử dụng SharedTypeResolver và các loại được chia sẻ giữa máy khách và máy chủ.

[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(); 
    } 
} 

Đây rõ ràng là một ví dụ đơn giản về sự cố tôi đang gặp phải. Một khách hàng có thể khá vui vẻ đẩy và bật BaseType hoặc DerivedType vì cả khách hàng và máy chủ đều biết về họ, nhưng nếu khách hàng đẩy UnsharedType mà dịch vụ không biết tôi gặp lỗi như bạn mong đợi.

Các định dạng ném một ngoại lệ khi cố gắng deserialize thông điệp : Có lỗi trong khi cố gắng deserialize tham số http://tempuri.org/:item. Thông báo InnerException là 'Lỗi ở dòng 1 vị trí 316. Yếu tố' http://tempuri.org/:item 'chứa dữ liệu từ loại bản đồ với tên' TestWcfClient, Phiên bản = 1.0.0.0, Văn hóa = trung lập, PublicKeyToken = null: TestWcfClient.UnsharedType ' . Các deserializer không có kiến ​​thức về bất kỳ loại bản đồ để tên này. Cân nhắc việc thay đổi cách triển khai phương thức ResolveName trên DataContractResolver của bạn để trả về giá trị không null cho tên 'TestWcfClient.UnsharedType' và không gian tên 'TestWcfClient, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'. ' . Vui lòng xem InnerException để biết thêm chi tiết.

suy nghĩ hiện tại của tôi là thêm IExtensibleDataObject để BaseType để giữ các giá trị từ một loại không chia sẻ và đưa ra một loại không chia sẻ giống như BaseType với dịch vụ trên deserialization khi một mục được đẩy; điều ngược lại sẽ cần phải xảy ra khi một vật phẩm xuất hiện. Tôi chỉ không chắc chắn làm thế nào để đi về nó. suy nghĩ của tôi cho đến nay về các phương pháp có thể:

  • tùy Hơn nữa để DataContractResolver mà có thể liên quan đến một TypeDelegator
  • Sử dụng IDataContractSurrogate ở vị trí của một loại không chia sẻ
  • Bằng cách nào đó giữ lại XML serialized dịch vụ nhận được khi một mục được đẩy , sau đó sử dụng điều này trong thư trả lời khi một mục được xuất hiện
  • Sử dụng trình kiểm tra thư để thao tác các thư

Tôi không biết liệu có bất kỳ thứ gì trong số này sẽ hoạt động hay không, những gì sẽ được tham gia hoặc giải pháp tốt nhất là gì. Bạn có?

+0

Bạn sẽ thay đổi các đối tượng trên dịch vụ hay bạn chỉ cần lưu trữ chúng và gửi lại cho khách hàng? – Enes

+0

Chỉ lưu trữ và gửi lại. Tôi cần biết về kiểu cơ sở vì nó có một số thuộc tính mà dịch vụ sử dụng để tác động đến bộ nhớ, nhưng nó chỉ đọc cho đến khi dịch vụ được quan tâm. – batwad

Trả lời

3

Tôi đã thực hiện một số tiến trình với điều này bằng cách sử dụng trình kiểm tra thư và loại trình giữ chỗ triển khai IExtensibleDataObject. Thanh tra maniuplates thông điệp gửi đến và thay đổi gợi ý kiểu thành của trình giữ chỗ và thêm kiểu gốc làm thuộc tính.Khi loại này được gửi đi trong một câu trả lời, điều ngược lại xảy ra, do đó làm cho trình giữ chỗ trông giống như kiểu gốc.

Khiếu nại của tôi với giải pháp này là nó bị ràng buộc với dịch vụ vì tôi phải bao gồm không gian tên XML của dịch vụ và đặt tên rõ ràng các phương thức và tham số được maniuplated. Khác hơn là dường như làm việc khá tốt, mặc dù tôi đã chỉ thử nghiệm nó trên các loại khá đơn giản có nguồn gốc từ BaseType.

Có ai có thể cải thiện điều này không? Có một tiền thưởng trong đó cho bạn.

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) 
    { 
    } 
} 
Các vấn đề liên quan