2017-11-06 10 views
2

に無効な列挙値を無視します。新しい値が追加され、その値が関数呼び出しによって返された場合、Webサービスのコンシューマは、そのenum値を気にしなくても例外をスローします。は、私が列挙型の配列を返すASP.NETのWebサービスのWebMethod属性を持つSOAPデシリアライズ

[WebMethod] 
public UserRole[] GetRoles(string token) 

部分のwsdl:

<s:simpleType name="UserRole"> 
    <s:restriction base="s:string"> 
     <s:enumeration value="Debug" /> 
     <s:enumeration value="EventEditor" /> 
     <s:enumeration value="InvoiceEntry" /> 
    </s:restriction> 
    </s:simpleType> 

(消費者は、このWSDLを使用してコンパイルされたが、その後、WSDLの変更、新しい値が現在許可されている - その値が返された場合、XML例外がでスローされますクライアント。)

私は、エラーをキャッチし、いずれかの配列からその項目を削除するか、またはデフォルト値に置き換えることができるように、このタイプのSOAPの直列化復元を無効にする方法はありますか?これがXMLの代わりにJSONを使用していた場合、そのタイプを処理するためにJsonConverterを登録することができます。同様のグローバルな "RegisterConverter"タイプの関数を探しています。私は存在しないと思っていますが、いくつかの助けを期待しています...

すべてのコードはwsdlによって生成され、Web参照が更新されると再生成されるため、属性付きEnumをデコレートする手段はありません。通常、wsdlによって生成されたクラスを変更したい場合は、部分クラスを作成できますが、Enumでは機能しません。そして、それがクラスであってもXmlSerializationコードをオーバーライドできるかどうかは確かではありません。



いくつかの追加の背景:

これは実際には、動的列挙型での私の試みとして実装されています。 wsdlはデータベースルックアップから生成され、データベースに余分な値を追加することができます。消費するアプリケーションは、Webサービスを再コンパイルせずに許容値にアクセスできます。このようにして、私はintellisenseとenum型の制約強制を得るが、Webサービスコードとクライアントコードを緊密に結合することなく値を追加することができる。問題は、新しい値を追加すると、新しいwsdlで更新されていない消費者を壊す可能性が生じるということです。消費者は何をするべきか分からないので、その値を無視するだけですとにかくそれです。

SOAP拡張機能はこれを修正する方法です(SOAP拡張機能をWebService自体に追加する方法は分かっていますが、クライアント側にSOAP拡張機能を追加する方法はわかりません)。しかし、私は本当にこれを簡単に処理する一般的な方法を持っているので、私は自分のコードでより動的な列挙型を持つことができます(実際には動的ではありませんが、値はWebサービスの中間層を通過する必要はありませんその中間層を再コンパイルする)。

+0

さて、2つの解決策が考えられました。 1つは、WebService宣言(一部のクラス内)でGetReaderForMessageをオーバーライドしてから、列挙型の名前と値をRegexで照合し、もう1つは受け入れ可能な値のリストをwebserviceまで送信し、webserviceを送信を拒否させることですそのリストにない値。それらの両方は吸う、と私は誰かが私がこれまで持っているものよりも良い答えを思い付くことを望んでいる。そうでなければ、私は明日自分のジャンキーの回答を投稿します。 –

+0

さて、私はGetReaderForMessageをオーバーライドしてしまいましたが、enum名のメッセージ文字列全体にマッチするRegexよりも若干優れていました...さらに、enumの逆シリアル化をオーバーライドするより良い方法が望まれます。 –

+0

WSDLとXSDの全体のポイントは、サービスとクライアントの間で契約を結ぶことです。この契約を破るために何かを実装しています。別の実装を再考するべきではありませんか?または、enumの代わりに何かを許可するようにWSDLを変更します。 –

答えて

0

「XmlSerialization.RegisterConverter(MyDynamicEnumType、DynamicEnum.Convert)」のようなものが理想的でしょう。他の人が回答を得たいと思っていますが、少なくとも私は来ましたRegex.Replaceを使って元の考え方より少し上手くいって、私が認識しなかった列挙型の値への参照を取り除きました。

私は、Webサービスのための部分的なクラスを使用し、以下のようにGetReaderForMessageをオーバーライドしています:

namespace Program.userws //note this namespace must match the namespace 
          //that the webservice is declared in (in auto-generated code) 
{ 
    partial class UserWebService 
    { 
     protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize) 
     { 
      return new EnumSafeXmlReader(message.Stream); 
     } 
    } 
} 

これはEnumSafeXmlReaderの定義です:

public class EnumSafeXmlReader : XmlTextReader 
{ 
    private Assembly _callingAssembly; 

    public EnumSafeXmlReader(Stream input) : base(input) 
    { 
     _callingAssembly = Assembly.GetCallingAssembly(); 
    } 

    public override string ReadElementString() 
    { 
     string typename = this.Name; 
     var val = base.ReadElementString(); 

     var possibleTypes = _callingAssembly.GetTypes().Where(t => t.Name == typename); 
     Type enumType = possibleTypes.FirstOrDefault(t => t.IsEnum); 

     if (enumType != null) 
     { 
      string[] allowedValues = Enum.GetNames(enumType); 

      if (!allowedValues.Contains(val)) 
      { 
       val = Activator.CreateInstance(enumType).ToString(); 
      } 
     } 

     return val; 
    } 
} 

私はまたのUserRoleの新しい値を追加しました - UserRole.Unknownを入力し、許可された値のリストの先頭にあることを確認します。それが認識されない場合

<s:simpleType name="AcctUserRole"> 
    <s:restriction base="s:string"> 
    <s:enumeration value="Unknown"/> 
    <s:enumeration value="Debug"/> 
    <s:enumeration value="EventEditor"/> 
    <s:enumeration value="InvoiceEntry"/> 
    </s:restriction> 
</s:simpleType> 

だから限り、この列挙の値は型名<UserRole>UnexpectedRole</UserRole>とタグに包まれているように、私のクライアントは喜んで無視することができ、UserRole.Unknownに置き換えられます。 UserRoleという別のタグがあって、この列挙型ではなく、たとえばstringまたはintと期待されていたタグがあった場合、これもまた破損する可能性があることに注意してください。それはかなり脆い。


このソリューションは、まだ希望する多くの葉が、それは一般的に列挙値のリスト上で動作します...

<GetRolesForUserResult> 
    <UserRole>InvoiceEntry</UserRole> 
    <UserRole>UnexpectedRole</UserRole> 
</GetRolesForUserResult> 

これはUserRole.InvoiceEntryUserRole.Unknownが含まれていUserRole[]が生じてしまいます。

しかし、私はタイプのUserRoleのフィールドまたはプロパティがある場合:

<User> 
    <ID>5</ID> 
    <Name>Zorak</Name> 
    <PrimaryRole>UnexpectedRole</PrimaryRole> <!-- causes an exception --> 
</User> 

のリーダーが「PrimaryRoleは」のUserRoleを入力してデシリアライズする必要があることを知る方法がないので、これはまだ、失敗します。 XmlSerializerはこれを知っていますが、わかる限り、XmlSerializerをオーバーライドする方法はなく、XmlReaderのみをオーバーライドする方法はありません。

EnumSafeXml Readerにenum型に逆シリアル化するタグを認識させるのに十分な情報を与えるのは完全に不可能ではないと思いますが、今のところ私は喜んで問題に直面しています。列挙型の値の配列です。これは現在行われています。

タイプにキャッシングを追加しました。タグ名を一度チェックして列挙型の名前であるかどうかを確認するだけですが、この例ではわかりやすくするために削除しました。


このソリューションを改善するために、他の解決策または推奨事項を歓迎します。

-1

私はdownvoteを危険にさらし、明白なことを述べます:サービス契約にenumを使用しないでください。あなたが確認したように、固定ドメイン以外は脆いです。

私がサービスの消費者であった場合、Unknownのエントリは、「サービスがこれを返すときに私は何をすべきでしょうか」と尋ねるでしょう。あなたが「心配しないで、クライアントの互換性のためにそこにいるのではないでしょうか?」というような返事をしてくれます。契約で何をしているのか分からないのですか?

string[]を返し、処理できる情報のためにクライアントが配列を解析するようにしてください。 Intellisenseが実際にあなたの目標である場合、クライアントの列挙型のサブセットを定義することができます。さらに複雑なソリューションを探すのに費やした時間の100倍を実装できました。 STEP。ああ。から。 THE。 ENUMS。

+0

これは有用な答えではありません。データに制約がある理由は十分あります。ルックアップテーブル、列挙型、文字列値、およびマジックナンバーの間の最良の答えは何年も探しています。ただ、彼らのどれもかなり働いていません。私は最後に、インテリセンス、データ制約、発見可能性、および拡張性を提供する方法でルックアップテーブルと列挙型をリンクする方法を持っています。XMLデシリアライズが必要です。理解する。私はそうする方法を考え出しましたが、私はよりエレガントなものを探しています。 –

+0

ルックアップテーブルはenumではありません。 "動的列挙型"は列挙型ではありません。サービス契約はインタフェースと同じではありません。あなたのデザインの微妙な不一致は、あなたが頭を壁に打ち付けた理由です。今はハンマーがあり、すべてが爪のように見えます。代わりにドライバーを使用してみてください。 – batwad

+0

ルックアップテーブル、列挙型、 "文字列型"変数、およびマジックナンバーはすべて同じ最終目標を持ちます。これは選択可能な値を表しています。彼らの誰もが単独で目標を完全に達成することはありません。制約、拡張性、可搬性、懸念の分離、発見性を同時に含む実装は誰もいないようです。それは難しい問題です。それは私がそれに苦しんでいる理由です。なぜなら、私は「ねじで打ちようとしているから」というわけではありません。私が今抱えている唯一の問題は、逆シリアル化コードをオーバーライドすることです。これは難しくありません。 –

関連する問題