2013-04-09 9 views
9

を保持は、私は私のサービスが受け入れて、実際にそれらのタイプがどうなるか知らなくてもBaseTypeから派生型を返すことができるようにしたいです。私はほとんどSharedTypeResolver from this excellent blog postに基づいてカスタムDataContractResolverを使用してソリューションを持っています。デシリアライズ誘導基の種類としてWCFサービス内の型が、型情報

パズルの欠けている部分は、サービスが処理するタイプがサービスと共有されていない可能性がありますが、それを受け入れて、そのタイプがどうあるべきかを認識したいということです。私はスタックとして機能する次のサービスの例を思いつきました。あなたはSharedTypeResolverを使用して、種類がクライアントとサーバの間で共有されてBaseType由来いずれかのタイプをプッシュして開くことができます。

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

これは明らかに私が抱えている問題の非常に単純化された例です。クライアントはかなり陽気にプッシュし、クライアントとサーバーの両方がそれらについて知っているのでBaseTypeまたはDerivedTypeをポップが、クライアントがサービスを知らないUnsharedTypeを押した場合、私はあなたが期待するようなエラーを取得することができます。 メッセージをデシリアライズしようとしているときに

フォーマッタが例外をスローしました:パラメータ http://tempuri.org/:itemをデシリアライズしようとしているときにエラーが発生しました。 'http://tempuri.org/:item'要素には、 'TestWcfClient、Version = 1.0.0.0、 Culture = neutral、PublicKeyToken = null:TestWcfClient.UnsharedType'という名前にマップされたタイプのデータ が含まれています。 。 デシリアライザは、この名前にマップの任意のタイプの知識を持ちません。 DataContractResolverのResolveNameメソッドの実装を、 'TestWcfClient.UnsharedType'および名前空間 'TestWcfClient、 Version = 1.0.0.0、Culture = neutral、PublicKeyToken = null'のnull以外の値を返すように変更することを検討してください。 。詳細については、 InnerExceptionを参照してください。

私の現在の考え方は、非共有タイプの値を保持し、非共有タイプは、アイテムがプッシュされ、デシリアライゼーション上のサービスへBaseTypeのように見えるようにBaseTypeIExtensibleDataObjectを追加することです。アイテムがポップされたときには、その逆が発生する必要があります。私はそれについてどうやって行くのか分かりません。アイテムが押されたときにサービスが受けシリアル化されたXMLを保持どういうわけか、非共有タイプの代わりにIDataContractSurrogateを使用してTypeDelegator

  • を伴う可能性があるDataContractResolver

    • さらにカスタマイズ:これまでのアプローチに私の考えアイテムが
    • ポップされたとき、その後、メッセージを操作するメッセージ・インスペクタを使用して応答でこれを使用

    これらのいずれかがうまくいくかどうか、何が関係するのか、何が最善の解決策なのかはわかりません。あなたは?

  • +0

    サービス上のオブジェクトを変更するのか、それともそれらを保存してクライアントに返すだけですか? – Enes

    +0

    保存して送り返してください。私は、サービスがストレージに影響を与えるために使用するいくつかのプロパティを持っているとして、基本型について知っておく必要がありますが、それは読み取り専用です限りのサービスを懸念しています。 – batwad

    答えて

    3

    メッセージインスペクタと、IExtensibleDataObjectを実装するプレースホルダタイプを使用して、これをいくつか進めてきました。検査官は、着信メッセージをmaniuplatesとプレースホルダとタイプヒントを変更し、プロパティとしてオリジナルタイプを追加します。タイプが返信で送信されると、逆のことが起こり、それによってプレースホルダが元のタイプのように見えます。このソリューションで

    私の苦情は、私は、サービスのXML名前空間を含め、明示的にmaniuplatedする方法やパラメータの名前を指定しなければならなかったので、それがサービスにバインドされていることです。それ以外で私が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) 
        { 
        } 
    } 
    
    関連する問題