ASP.NETのWebサービスにはまる

2日続けて重い話を書いてしまいましたので、気分を変えてプログラミングの話題といってみましょう。例によって「新館」絡みの話ですが、「写真館」で見てもらえた回数を写真ごとに記録しようかと思いました。でもまぁ、何か新しいことをやろうとすると、思いもよらないことにはまるものです。今回も、数時間にわたってはまってしまいましたが、謎が解ければシンプルな話。

Shinkan_02

まずは、個々の写真の情報を管理するテーブルに、閲覧回数を表す整数型(int)のフィールドを新たに設けました。

view_count   int

写真が閲覧されたときに、カウントアップするためにはストアドプロシージャを用いることにしました。

CREATE PROCEDURE ViewCountUp
(
@id int
)

写真のIDを指定して呼び出すと、view_countの内容を1増やします。

このストアドプロシージャを呼び出すためのWebサービスを定義します。

Photo.ViewCountUp(string id)

名前空間は仮にPhotoとしておきますが、そこのViewCountUpを呼び出すと上のストアドプロシージャが呼び出されるようにします。ViewCountUpストアドプロシージャに渡すためのidを、ここでも引数で受け取ることにします。

さて、「写真館」では、写真の表示にFancyBoxというjQueryのプラグインを使用していますが、これには当然JavaScriptないしjQueryが絡んできます。FancyBoxで写真を表示する仕組みとして、セレクタを指定してfancybox()メソッドを呼び出すのですが、その際のセレクタには「a.image」のようにa要素のimageクラス、というように指定していますので、このタイミングで上のWebサービスを呼び出せばよいことになります。

このとき、Webサービスに渡すidをどうやって入手するか?ということですが、写真のサムネールを表示する際に、隠しフィールドであるHiddenFieldコントロールに埋め込んでおくことにしました。この値を取り出すにはセレクタでの指定が必要ですが、HiddenFieldコントロールにはCssClass属性がないようなので、周囲をspan要素で挟み、そちらでクラスを指定することにしました。

<span class="image"><asp:HiddenField … value="<%#…..%>" /></span>

これで、セレクタに「span.image :hidden」を指定すれば、val()メソッドを適用させてidを取得できます。

var id = $("span.image :hidden").val();

ようやく本題ですが、imageクラスのa要素をクリックした際にWebサービスを呼び出すために、こんな感じでready()イベントのハンドラ内に書いてみます。

        $(function () {
            $(‘a.image’).click(
                function () {
                    var s = $(‘span.image :hidden’).val();
                    Photo.ViewCountUp(s,
                        function (result, cx, name) { /*alert(result);*/ },
                        function (ext, cx, name) { /*alert(ext.get_message());*/ }
                    );
                }
            );
        });

これに合わせて、fancybox()メソッドを呼び出すために、直後にこれを書いておきます。

        $(‘a.image’).fancybox();

ところが、動作を切り分けるために、fancybox()メソッドの呼び出しはコメントアウトして無効にしてしまいました。これがまずかったのです。試しに実行してみると、サムネールをクリックしてWebサービスが呼び出されて、写真が表示されるのですが、そのときに「サーバーメソッド "ViewCountUp" の呼び出しに失敗しました」と出てくるのです。あれれ?うまく動いていないみたいです。

試しに、Webサービス側で呼び出されたことを確認できるようにDebugメソッドを埋め込んでおきましたが、これが呼び出された形跡はありません。試しに、Webサービスを直接呼び出してみましたが、これは正常に動作します。すると、呼び出す手前でおかしなことになっていることになります。

ここで七転八倒していたのですが、わかってみれば原因はあっさりしています。つまり、サムネールのクリックでWebサービスが呼び出されますが、これは非同期であるので、呼び出しから帰ってくる前に次の処理に進みます。つまり、サムネールに対応するa要素のリンク先であるイメージの表示に切り替わるのですが、これだとWebサービスの呼び出しから帰ってくる場所がありません。

実際はこんな単純ではないでしょうが、ページの遷移を伴う場合に、Webサービスの呼び出しは御法度、ということでした。

ちなみに、これに気付いたのは、上のclickイベントハンドラの戻り値をfalseにしてクリックを無効にしたら、あっさり動いたからでした。さらに言えば、fancybox()メソッドの呼び出しを有効にしたらあっさり動きました。これはつまり、fancybox()メソッドでは画面の遷移がないので、Webサービスの呼び出しに何の問題もないというわけです。

わかってみればあっけない、というお話でした。

コメント