Silverlight 1.0 + ASP.NET AJAX : 試行錯誤を減らすための参考情報 :ScriptingJsonSerializationSectionクラスのMaxJsonLengthプロパティのサイズ設定をお忘れなく

※ 調査したところ、本件は、タイムアウト(時間の問題)とJSONでの文字列長が既定値を超えている場合(データ転送量の問題)の両方が混じっているのですが、この投稿ではデータ量だけに着目して一部修正してます。 

現在、社内プロジェクトでSilverlightを使ったアプリケーションを作っていることは以前からご紹介しているのですが、開発が進むにつれ、いろんな落とし穴に遭遇します。先週見つけた問題で、今は解決していることをご紹介します。

問題だったのは、Webサービス側で正常にデータを返しているメソッドをScriptManagerとJavaScriptを利用して、クライアントから呼び出したときに、なぜかエラーになってしまうという現象でした。Webサービス側でテストすると異常ではなさそうですので、サーバ側では原因がつかめません。ましてや、XAMLデータを受け取っているので、XAMLの中の問題かと思い、戻り値を使ってSilverlight内で表示させてみるとうまくいくのです。頭の中は「???」でいっぱいになります。現在作っているアプリケーションはいったん生成したXAMLデータをデータベースにキャッシュしているので、キャッシュ情報を確認すると正常に生成できています。切り分けのために、サーバでの処理時間の問題はいったんここでは無視して、転送されるデータ量だけに着目してみたいと思います。

JavaScriptの文字列で何かが起きているのは間違いなさそうなのですが、まずは、Webサービス側から送られているデータ量を確認してみました。単純なANSIテキストとして保存すると121KBを超えています。30KBとか40KBあたりではエラーになりません。うーむ・・・。
問題の原因を知らないでいると、Webサービスの呼び出しが失敗した原因を間違って解釈してしまい、デバッグが遠回りになる可能性があるかもしれません。もっと明確に問題を定義してみましょう。

ASP.NET AJAXを使い、ScriptManager経由でWebサービスをJavaScriptからアクセスしようと設定する場合、その裏では、JSONを使って通信が行われています。script経由のWebサービスの呼び出しの戻り値の大きさが小さいデータ量の場合には問題にならないのですが、web.configなどで構成を変えていない場合、Webサービス側は正常にデータを返しているにもかかわらず、手元の事例で見るとANSIテキスト換算でサイズが121KBを超えているときにWebサービスの呼び出しが失敗します。この問題の原因を考えます。ただし、サーバでの処理時間についてはここでは考えず、タイムアウトやネットワークの障害が起きていないことを前提にします。

最初はこの問題に対して、Webサービスが返すデータを極力最適化して、サイズを小さくしようと試みました。これは応答速度の改善には効果があります。しかし、やっているうちにデータが劇的に減らせないとわかった場合、それでは明確な解決にはならないと判断できます。これに対して、何らかの構成があるだろうと想定しました。ネット上を検索してみたのですが、解答のヒントはあるものの、それがなぜか、何をどう変えればいいのか、余裕のない状況の頭では即断できません。

わかってしまえば躓くことはありませんが、この問題を解決するには、ScriptingJsonSerializationSectionクラスによる構成が必要です。設定すべきプロパティは、MaxJsonLengthです。このプロパティの既定値は、102400 文字になっています。このため、この文字数を超えたデータを受け取ろうとすると、JSONによる通信部分がエラーとなります。(ドキュメントを追いかけるときに大変紛らわしいので、補足しておきますが、JavaScriptSerializerというクラスもあり、そちらにもMaxJsonLengthがあり、そちらの既定値は、2097152文字になってます。2バイトUnicodeで約4MBのデータです。)

これで上述の121KBの問題の原因はつかめました。

何にせよ、解決方法ですが、次の内容をweb.configに追加してください。数字部分は、要件に応じて変更していただければと思います。

  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="204800" />
      </webServices>
    </scripting>
  </system.web.extensions>

ASP.NET AJAX Enabled Web Applicationのプロジェクトテンプレートを利用して開発をしていれば、下記のようなエントリーがweb.configに含まれているのですが、私の場合、web.configを手作業で構成している際に、別のもので上書きしてしまい、この問題に気がつくのが遅くなりました。普通にテンプレートが使われていれば、コメント部分をはずして書き換えればよいです。

<system.web.extensions>
    <scripting>
      <webServices>
      <!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
      <!--
      <jsonSerialization maxJsonLength="500">
        <converters>
          <add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
        </converters>
      </jsonSerialization>
      -->
      <!-- Uncomment this line to enable the authentication service. Include requireSSL="true" if appropriate. -->
      <!--
        <authenticationService enabled="true" requireSSL = "true|false"/>
      -->

      <!-- Uncomment these lines to enable the profile service. To allow profile properties to be retrieved
           and modified in ASP.NET AJAX applications, you need to add each property name to the readAccessProperties and
           writeAccessProperties attributes. -->
      <!--
      <profileService enabled="true"
                      readAccessProperties="propertyname1,propertyname2"
                      writeAccessProperties="propertyname1,propertyname2" />
      -->
      </webServices>
      <!--
      <scriptResourceHandler enableCompression="true" enableCaching="true" />
      -->
    </scripting>
  </system.web.extensions>

 ・・・

Silverlightを使って、バックエンドのデータソースとやり取りするようになると、単純に静的なXAMLファイルを読み込むだけではなく、Webサービスによって動的にXAMLを生成する可能性が出てきます。XAML内の要素数が増えてくれば、それだけデータ量も大きくなります。UIElementの既定値をうまく利用して生成するXAMLのデータ量を減らす一方で、このMaxJsonLengthの設定もご確認ください。

参考になれば幸いです。

一方で、この問題を見つけた環境において、サーバでの処理時間が長い場合にWebサービスの呼び出しが正常に終了せず、Status Codeが500となる現象をつかんでいます。こちらについては、別途、投稿したいと思います。