WCF AJAX サービスを jQuery を使用して呼び出すサンプルを掲載します。本サンプルではASP.NET AJAX は使用していません。通信に使用するデータ形式はJSON形式です。

ASP.NET を使用せずに WCF AJAX サービスを作成する方法
http://msdn.microsoft.com/ja-jp/library/bb885100.aspx
AJAX の統合と JSON のサポート
http://msdn.microsoft.com/ja-jp/library/bb412173.aspx

ASP.NET AJAX 用の WCF AJAXサービスの作成は次のURLが参考になります。

ASP.NET AJAX 用の WCF サービスの作成
http://msdn.microsoft.com/ja-jp/library/bb412167.aspx
WCF Sample 017:WCF で AJAX Serviceを作成してみる。 
http://www.pine4.net/ComponentGeek/ShowArticle/30.aspx

動作確認環境を記載します。

  • Visual Studio 2010 で作成した WCF 4.0 サービス
  • データ形式: JSON
  • クライアント Internet Explorer 9.0
  • HTMLページは 標準モード
  • jQuery のバージョン 1.7.1

IEで動作が標準モードになるように html を作成しています。IEがQuirks (互換) モードで動作している場合は、サンプルで使用している JSON.stringify メソッド(使用していないが、parseメソッドも)が使用できません。またIE7以前のブラウザにもメソッドは存在しません。 別途 json2.js をhtml内で参照するか、Firefox など別のブラウザを使用して動作を確認する必要があります。

1. サンプルプログラムの作成

空のソリューションの作成。WCFサービスライブラリを作成し、WCFサービスをホストするWEBアプリケーションの作成の順にサンプルを構築します。

1.1 空のプロジェクトを作成

Visual Studio 2010 を起動し、下図のように空のソリューションを作成します。

1.2 WCF サービスライブラリの作成

作成したソリューションに WCF サービスライブラリプロジェクトを追加します。名前は ProductService としています。

作成したプロジェクトのプロパティ画面を開き、アプリケーションタブの 対象のフレームワークを .NET Framework 4 Client Profile から .NET Framework 4に変更します。次にプロジェクトを右クリックしコンテキストメニューの参照の追加をクリックします。参照の追加ダイアログで 下図のように System.ServiceModel.Web を追加します。

次に、既定で作成されたIService1.cs, Service1.cs を削除します。

前準備は完了です。簡単なデータコントラクトクラスとサービスコントラクトを実装します。

プロジェクトに下図のように新しいクラスを追加します。名前はProduct としています。

Productクラスは クライアントとサービスの間で通信に使用するデータコントラクトクラスです。次のように編集します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace ProductService
{
    [DataContract]
    public class Product
    {
        [DataMember]
        public int ProductId { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public decimal UnitPrice { get; set; }
    }
}

次にサービスコントラクトを作成します。プロジェクトに下図のようにインターフェイスを追加します。名前はIProductService としています。

IProductService は サービスコントラクトクラスです。本サービスコントラクトをクライアントからjQuery の ajax メソッド経由で取得します。作成されたIProductService.cs を次のように編集します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace ProductService
{
    [ServiceContract]
    interface IProductService
    {
        /// <summary>
        /// プロダクトを検索する
        /// </summary>
        /// <returns></returns>
        [OperationContract]
        [WebGet(ResponseFormat=WebMessageFormat.Json)]
        IEnumerable<Product> RetrieveAllProduct();
        /// <summary>
        /// プロダクト名でプロダクトを検索する
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        Product RetrieveProductByName(string name);
        /// <summary>
        /// プロダクトを追加する
        /// </summary>
        /// <param name="item"></param>
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddProduct(Product item);
        /// <summary>
        /// プロダクトを複数追加する
        /// </summary>
        /// <param name="items"></param>
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void AddProducts(Product[] items);
        /// <summary>
        /// プロダクトを更新する
        /// </summary>
        /// <param name="productId"></param>
        /// <param name="price"></param>
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
        void UpdateProduct(int productId, string name, decimal price);
    }
}

 本プロジェクトの最後に、サービスコントラクトの実装クラスを作成します。下図のように新規クラスを作成します。名前は ProductService.cs としています。

作成したProductService.cs を次のように編集し、サービスコントラクトを実装します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ProductService
{
    /// <summary>
    /// IProductServiceの実装。処理に意味はない。
    /// </summary>
    public class ProductService : IProductService
    {
        List<Product> products;
        public ProductService()
        {
            // 適当にサンプルデータ作成
            products = new List<Product>
            {
                new Product{ ProductId = 1, Name = "Car", UnitPrice=1000},
                new Product{ ProductId = 2, Name = "Bicycle", UnitPrice=40000},
                new Product{ ProductId = 3, Name = "Train", UnitPrice=5000000}
            };
        }
        public IEnumerable<Product> RetrieveAllProduct()
        {
            return products;
        }

        public Product RetrieveProductByName(string name)
        {
            var query = from p in products
                        where p.Name == name
                        select p;

            return query.FirstOrDefault();
        }

        public void AddProduct(Product item)
        {
            products.Add(item); // 特に意味のあるコードではない
        }
        public void AddProducts(Product[] items)
        {
            foreach (var item in items)
            {
                products.Add(item);
            }
        }
        public void UpdateProduct(int productId, string name, decimal price)
        {
            // do nothing
        }
    }
}

1.3 WCFサービスをホストするWebアプリケーションとクライアントのhtml作成

サービスコントラクトを作成したので、サービスをホストするASP.NET Web アプリケーションを作成します。ソリューションに 下図のように ASP.NET 空の Webアプリケーション を作成します。名前は ProductService.Web としています。

次のjQuery を用意します。CDNでホストされているjQuery を参照したり、 jQuery をダウンロードしてもよいのですが今回は NuGet を使用します。NuGetを起動して Online から jQuery を検索して jQuery をInstall します。本記事では 1.7.

jQuery をダウンロードすると、下図のようにScripts フォルダが作成され、その下にjquery ファイルが作成されます。

次にWebアプリケーションプロジェクトを右クリックしコンテキストメニューの参照の追加から、 作成したWCFサービスライブラリプロジェクト(本例ではProductService)への参照を追加します。

次に、Webアプリケーションプロジェクトに svc ファイルを作成します。新しい項目を追加します。テキストファイルをテンプレートとしてファイルを作成します。ファイル名はProduct.svc としています。

作成した Product.svc を次のように編集します。

<%@ServiceHost Language="c#" Debug="true" Service="ProductService.ProductService" Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

サーバーの設定の最後にWeb.config を編集します。次のように system.serviceModel を編集します。

サンプルではWeb.configにエンドポイントの設定をしていますが、これは通信時に詳細なエラー情報がクライアントに返されるように serviceDebug の includeExceptionDetailInFaults を true にしたサービスビヘイビアを設定したかったためです。既定の動作で問題ない場合はエンドポイントの設定をWeb.config にしなくても本サンプルは動作します。

<?xml version="1.0" encoding="utf-8"?>

<!--
  ASP.NET アプリケーションの構成方法の詳細については、
  http://go.microsoft.com/fwlink/?LinkId=169433 を参照してください
  -->

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ajaxServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="ajaxBehavior">
          <!--<webHttp />-->
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="ProductService.ProductService" behaviorConfiguration="ajaxServiceBehavior">
        <endpoint address="" behaviorConfiguration="ajaxBehavior" binding="webHttpBinding"
				 bindingConfiguration="" contract="ProductService.IProductService" />
      </service>
    </services>
    <bindings />
  </system.serviceModel>

</configuration>

jQuery を使用する html コンテンツは作成していませんが、現時点で動作確認は行えます。Webアプリケーションをデバッグ実行し、 Product.svc/RetrieveAllProduct にアクセスします。うまく動作している場合IE9の場合 JSON 形式のファイルがダウンロードされます(下図参照)。これはRetrieveAllProduct に WebGet Attribute をアノテートしているため本動作確認が行えています。

動作していることが確認できたら、クライアントからWCF Ajax サービスを呼び出してみます。Webアプリケーションプロジェクトに新しい項目を追加します。HTMLページをテンプレートとして選択しファイルを追加します。本例では HTMLPage1.html としています。

html を次のように編集しています。冒頭で記述したように IE の 標準モードで動作するように DOCTYPE 宣言を <!DOCTYPE html> としています。互換モードで動作すると json2.js を別途参照するようにしないと、JSON.stringify メソッドでエラーとなるので注意してください。オンロード時に RetrieveAllProduct を呼び出すようにしています。また、各ボタンが押されたときに、 サービスメソッドを呼び出しています。InvokeUpdateProduct ファンクションでは 手動で json 形式の文字列を作成しています。

<!DOCTYPE html>
<!-- IE8以上 標準モードで動作させることが前提 -->
<!-- Quirks(クアークス)モードで動作させる場合は json2.js がJSON.stringify/parse メソッドを使用するのに必要 -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>WCF Service jQuery Integration</title>
    <script type="text/javascript" src="Scripts/jquery-1.7.1.js"></script>
    <script type="text/javascript">
        $(function () {
            // Getで取得するテスト
            $.ajax({
                type: "Get",
                datatype: "json",
                url: "Product.svc/RetrieveAllProduct",
                data: null,
                contentType: "application/json; charset=utf-8",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Accept", "application/json")
                },
                success: function (data, textStatus, xhr) {
                    for (var i = 0; i < data.length; ++i) {
                        $("#list").append(data[i].ProductId).append(data[i].Name).append(data[i].UnitPrice);
                        $("#list").append("<br>");
                    }
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr);
                }
            });
        });
        function InvokeRetrieveProductByName() {
            var obj = new Object();
            obj.name = "Car";

            var arg = JSON.stringify(obj);
            $.ajax({
                type: "Post",
                datatype: "json",
                url: "Product.svc/RetrieveProductByName",
                data: arg,
                contentType: "application/json; charset=utf-8",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Accept", "application/json")
                },
                success: function (data, textStatus, xhr) {
                    if (data != null) {
                        $("#list").append(data.ProductId).append(data.Name).append(data.UnitPrice);
                        $("#list").append("<br>");
                    }
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr);
                }
            });
        }
        function InvokeAddProduct() {
            var obj = {
                item: { Name: "NewName",
                    ProductId: 100
                }
            };

            var arg = JSON.stringify(obj);
            $.ajax({
                type: "Post",
                datatype: "json",
                url: "Product.svc/AddProduct",
                data: arg,
                contentType: "application/json; charset=utf-8",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Accept", "application/json")
                },
                success: function (data, textStatus, xhr) {
                    alert("Added");
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr);
                }
            });
        }
        function InvokeAddProducts() {
            var obj = { items : new Array(2)};
            obj.items[0] = { Name: "NewName1", ProductId: 201, UnitPrice: 30 };
            obj.items[1] = { Name: "NewName2", ProductId: 202, UnitPrice: 33 };

            var arg = JSON.stringify(obj);
            $.ajax({
                type: "Post",
                datatype: "json",
                url: "Product.svc/AddProducts",
                data: arg,
                contentType: "application/json; charset=utf-8",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Accept", "application/json")
                },
                success: function (data, textStatus, xhr) {
                    alert("Added");
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr);
                }
            });
        }
        function InvokeUpdateProduct() {
            // 手動でjson形式の文字列を設定する場合
            var arg = '{ "productId" : "20" , "name" : "UpdatedName" , "price":"20.0"}';
            $.ajax({
                type: "Post",
                datatype: "json",
                url: "Product.svc/UpdateProduct",
                data: arg,
                contentType: "application/json; charset=utf-8",
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("Accept", "application/json")
                },
                success: function (data, textStatus, xhr) {
                    alert("Updated");
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr);
                }
            });
        }
        
    </script>
</head>
<body>
    <div id="list">
    </div>
    <input type="button" onclick="InvokeRetrieveProductByName()" value="検索" />
    <input type="button" onclick="InvokeAddProduct()" value="追加" />
    <input type="button" onclick="InvokeAddProducts()" value="複数追加" />
    <input type="button" onclick="InvokeUpdateProduct()" value="更新" />
</body>
</html>

以上で準備が整いました。本サンプルでは、ただデータを取得するだけでなく、引数にデータコントラクトクラスや配列を指定したり、GETやPOSTを使用してWCF AJAX サービスを呼び出しています。

デバッグモードでプロジェクトを起動し、下図のように動作の確認をします。

2. まとめ

説明は以上です。本例では、ただデータをGETするだけでなく、引数に配列やデータコントラクトクラスを使用しての動作確認もしています。WCF AJAX サービスを ASP.NET AJAX 以外から呼び出す場合、最初に呼び出せるようになるまで多少試行錯誤があると思います。本例を参考にすればほとんどのケースに対応できると思います。