jQuery (JavaScript) から C# のメソッドを呼び出す [レコード配列戻り値編]

# はじめに
Visual Studio を使えば `Web ブラウザ`上で動作する JavaScript から `Web サーバー`上にある C# のコード(メソッド)を容易に呼び出すことができます。
実際に動作するコードを見れば、簡単にエレガントにリモート呼び出し(Ajax 呼び出し)ができることがわかるでしょう。

* jQuery から C# のメソッドを呼び出す基本的なやり方は[こちらの記事](http://qiita.com/akmiyoshi/items/16c4a65731f8ec32e233)を御覧ください。

* Ajax+JSONPでクロスドメインに外部APIと連携する方法は[こちらの記事](http://qiita.com/akmiyoshi/items/64ea9c3b7d8ed0de4eb9)を御覧ください。

* 今回は、レコードの配列を返す C# メソッドの呼び出しサンプルをご紹介します。

# テーブル定義
```sql
CREATE TABLE [Employees] (
  [Employee ID] int IDENTITY (1,1) NOT NULL
, [Last Name] nvarchar(20) NOT NULL
, [First Name] nvarchar(10) NOT NULL
, [Title] nvarchar(30) NULL
, [Birth Date] datetime NULL
, [Hire Date] datetime NULL
, [Address] nvarchar(60) NULL
, [City] nvarchar(15) NULL
, [Region] nvarchar(15) NULL
, [Postal Code] nvarchar(10) NULL
, [Country] nvarchar(15) NULL
, [Home Phone] nvarchar(24) NULL
, [Extension] nvarchar(4) NULL
, [Photo] image NULL
, [Notes] ntext NULL
, [Reports To] int NULL
);
```

# レコードオブジェクトの定義
クラス(RecEmployee)の定義です。クラス名に DataContract 属性がつけてあります。各フィールド(プロパティ)には DataMember 属性をつけています。このクラスをレコードの型としてjQuery (JavaScript) と C# の間でデータの受け渡しを行います。

```csharp:RecEmployeecs
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;

namespace Northwind
{
    [DataContract]
    public class RecEmployee
    {
        public RecEmployee()
        {
        }
        public RecEmployee(Employee e)
        {
            PropUtil.CopyToFrom(this, e);
        }
        [DataMember]
        public int Employee_ID { get; set; }
        [DataMember]
        public string Last_Name { get; set; }
        [DataMember]
        public string First_Name { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public Nullable<System.DateTime> Birth_Date { get; set; }
        [DataMember]
        public Nullable<System.DateTime> Hire_Date { get; set; }
        [DataMember]
        public string Address { get; set; }
        [DataMember]
        public string City { get; set; }
        [DataMember]
        public string Region { get; set; }
        [DataMember]
        public string Postal_Code { get; set; }
        [DataMember]
        public string Country { get; set; }
        [DataMember]
        public string Home_Phone { get; set; }
        [DataMember]
        public string Extension { get; set; }
        //[DataMember]
        //public byte[] Photo { get; set; }
        [DataMember]
        public string Notes { get; set; }
        [DataMember]
        public Nullable<int> Reports_To { get; set; }
    }
    public class PropUtil
    {
        public static void CopyToFrom(object o1, object o2)
        {
            if (o1 == null) return;
            if (o2 == null) return;
            Type t1 = o1.GetType();
            Type t2 = o2.GetType();
            PropertyInfo[] members = t1.GetProperties();
            foreach (PropertyInfo p in members)
            {
                try
                {
                    t1.InvokeMember(
                        p.Name,
                        BindingFlags.SetProperty,
                        null,
                        o1,
                        new object[] {
                        t2.InvokeMember(p.Name, BindingFlags.GetProperty, null, o2, null)
                    });
                }
                catch (Exception /*ex*/)
                {
                    Debug.WriteLine(p.Name + "のコピーに失敗しました。");
                }
            }
        }
    }
}
```

# 呼び出される側(C#)のコード

```csharp:Service1.svc.cs
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

namespace Northwind
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class Service1
    {
        // 追加の操作をここに追加して、[OperationContract] とマークしてください
        [OperationContract, WebInvoke(Method = "GET")]
        public List<RecEmployee> SelectEmployees(int maxResults)
        {
            WebOperationContext.Current.OutgoingResponse.Headers.Add(
                "Cache-Control", "no-cache");
            System.Diagnostics.Debug.WriteLine("[maxResults]=" + maxResults);
            List<RecEmployee> result = new List<RecEmployee>();
            int count = 0;
            using (var db = new NorthwindEntities())
            {
                foreach (var emp in db.Employees)
                {
                    if (count >= maxResults) break;
                    RecEmployee rec = new RecEmployee();
                    PropUtil.CopyToFrom(rec, emp);
                    result.Add(rec);
                    count++;
                    System.Diagnostics.Debug.WriteLine("[count]="+count);
                }
            }
            return result;
        }
    }
}
```

# 呼び出す側(JavaScript/jQuery)のコード

```html:Views/Home/Index.cshtml
@{
    ViewBag.Title = "AJAX テスト";
}
<h3>@ViewBag.Title</h3>
<input id="buttonSelectEmployees" type="button" value="buttonSelectEmployees" />
<br />
<b>戻り値</b><div id="result"></div>
@section scripts {
<script>
    function onSuccess(data) {
        $("#result").text(JSON.stringify(data.d));
    }
    function onError(xhr) {
        alert("Error: " + xhr.responseText);
    }
    $("#buttonSelectEmployees").click(function () {
        $.ajax({
            type: "GET",
            url: '@VirtualPathUtility.ToAbsolute("~/Service1.svc/SelectEmployees")',
            data: { maxResults: 5 },
            success: onSuccess,
            error: onError
        });
    });
</script>
}
```