2012年11月30日金曜日

[C#] async, await

メソッドの宣言に async を付けると非同期メソッドとなり、メソッドの中で
awaitが指定できるようになる。
await を指定すると非同期メソッドの実行が終わるまで待ってから制御を戻す。

分かりやすい参考サイト
http://ufcpp.net/study/csharp/sp5_async.html#async


HttpClientクラスを使って、asyncとawaitを試してみる。

■どこからかコピペしてきたりしてつくった、おれおれサンプル

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("読み込みを開始します");

            // 非同期で実行
            readWebAsync("http://www.google.co.jp/").ContinueWith(
                (task) =>
                {
                    // task.Result に読み込んだ文字列が入ってる
                    Console.WriteLine(task.Result.Count() + "文字読み込みました。");
                });

            Console.WriteLine("読み込みを待っています");
            Console.ReadKey();
        }

        private static async Task<string> readWebAsync(string url)
        {
            HttpClient client = new HttpClient();
            var res = await client.GetStringAsync(url);
            Console.WriteLine("読み込み完了!");
            return res;
        }
    }



■実行結果
読み込みを開始します
読み込みを待っています
読み込み完了!
46568文字読み込みました。



readWebAsyncメソッドを変えて、応答ステータスコードを取得してみる

        private static async Task<string> readWebAsync(string url)
        {
            HttpClient client = new HttpClient();

            var res = await client.GetAsync(url);

            Console.WriteLine("リクエスト送信&レスポンス受け取り完了!");

            // 成功ステータスコードがチェックする
            res.EnsureSuccessStatusCode();

            Console.WriteLine("ステータスコード:" + res.StatusCode);

            Console.WriteLine("リクエスト内容:" + res.RequestMessage);

            var result = await res.Content.ReadAsStringAsync();

            Console.WriteLine("読み込み完了!");

            return result;
        }



■変更後の実行結果
読み込みを開始します
読み込みを待っています
リクエスト送信&レスポンス受け取り完了!
ステータスコード:OK
リクエスト内容:Method: GET, RequestUri: 'http://www.google.co.jp/', Version: 1.1, Content: <null>, Headers:
{
}
読み込み完了!
46648文字読み込みました。



どうでもいいけど Mainメソッドに async をつけようとしたらコンパイル時に
「'async'修飾子を指定できないエントリポイント」と怒られた(´・ω・`)


ついでにHttpClientについての参考ブログ
http://shiba-yan.hatenablog.jp/entry/20120822/1345563275

2012年11月15日木曜日

[ASP.NET MVC] JSONを受け取る、JSONを返す


2012/12/21 コードが間違っていたので修正 orz


ASP.NET MVC3以上ならJSONをモデルバインディングしてくれるようだったのでやってみる

参考サイト:JavaScriptおよびAJAXの改善点(下の方)
http://www.atmarkit.co.jp/fdotnet/scottgublog/20100811aspnetmvc3/aspnetmvc3.html


適当にJSONを送りつける画面を作った


■ビュー(の一部)

2012/12/21 赤文字の部分を変更
変更前はJSONじゃなくてフォームデータ形式でデータを送信していた模様…

<input type="button" value="JSON送信" onclick="json_post();" />

<script type="text/javascript">
    function json_post() {
        var url = '@Url.Action("JSONTest")';

        var post_data =
            {
                Grade : 1
                , Class : 2
                , Name: "クライアントさん"
                , Results: [
                    { Subject: "数学", Score: 64 }
                    , { Subject: "理科", Score: 70 }
                    , { Subject: "英語", Score: 80 }
                ]
            };


        //var post_data =
        //    {
        //        "Grade": "1"
        //        , "Class": "2"
        //        , "Name": "クライアントさん"
        //        , "Results[0].Subject": "数学"
        //        , "Results[0].Score": 90
        //        , "Results[1].Subject": "理科"
        //        , "Results[1].Score": 88
        //        , "Results[2].Subject": "英語"
        //        , "Results[2].Score": 51
        //    };


        $.ajax(
            {
                url: url,
                type: 'POST',
                data: JSON.stringify(post_data),   // JSONの文字列に変換
                dataType: 'json' 
                contentType:'application/json',    // content-typeをJSONに指定する
                error: function () { },
                complete: function (data) { alert(data.responseText) },
            }
        );
    }
</script>


こんな画面
















ボタンをぽちっと押すと下記のアクションにJSONを送りつける


■コントローラー(の一部)

        [HttpPost]
        public ActionResult JSONTest(SchoolViewModel viewModel)
        {
            // クライアントからJSONを受け取って出力ウィンドウに出してみる
            System.Diagnostics.Trace.WriteLine(viewModel.Grade + "年");
            System.Diagnostics.Trace.WriteLine(viewModel.Class + "組");
            System.Diagnostics.Trace.WriteLine(viewModel.Name);
            foreach (var result in viewModel.Results)
            {
                System.Diagnostics.Trace.WriteLine(result.Subject + ":" + result.Score + "点");
            }

            // クライアントに適当なJSONデータを返す
            var res = new SchoolViewModel()
            {
                Grade = 2
                ,
                Class = 3
                ,
                Name = "サーバーさん"
                ,
                Results = new List<TestResult>() {
                    new TestResult() { Subject = "数学", Score = 64 }
                    , new TestResult() { Subject = "理科", Score = 70 }
                    , new TestResult() { Subject = "英語", Score = 80 }
                }
            };
            return Json(res);
        }

バインドするクラスはこんな感じで何の変哲もないViewModel的なクラス。

    public class SchoolViewModel
    {
        public int Grade { get; set; }
        public int Class { get; set; }
        public string Name { get; set; }
        public List<TestResult> Results { get; set; }
    }

    public class TestResult
    {
        public string Subject { get; set; }
        public int Score { get; set; }
    }

実行した結果


■クライアントから送信したJSONの内容をVisualStudioの出力ウィンドウに出してみたもの
1年
2組
クライアントさん
数学:90点
理科:88点
英語:51点


■サーバーから返されたJSON








やったね!ちゃんと受け取れてるし、JSON返してるし!



↓2012/12/21 削除 そもそもJSONとして送ってなかった。ううう…ごめんなさい。

JSONをコレクションにモデルバインディングする時の注意


最初、コレクションに対応する、クライアントから送信するJSONは、

"Results": [ { "Subject[0]": "数学", "Score": 95 }, { "Subject": "英語", "Score": 51 } ]

のように配列で書いていたのだが、これではListにバインドしてくれなかった。


下記のサイトによると
http://msdn.microsoft.com/ja-jp/magazine/hh781022.aspx

> 少々直観に反することですが、JSON 要求にも同じ要件があります。つまり、JSON 要求もフォーム ポストの名前付け構文に従っている必要があります。
> 既定の値プロバイダーやモデル バインダーでは、データを JSON のフォーム ポストとして表す必要があります。

>    {
>      "UnitPrice[0].Code": "USD",
>      "UnitPrice[0].Amount": 100.00,
>    
>      "UnitPrice[1].Code": "EUR",
>      "UnitPrice[1].Amount": 73.64
>    }


どうやら配列のまま送れないらしい('A`)
フォームデータのように番号をつけないといけないようだ。
そのくせサーバーから返るJSONはしっかり配列になっているので、なんだか納得いかないのだった。



モデルバインディングしなくてもResponse.InputStreamから自力でデータを取り出して
DataContractJsonSerializerでデシリアライズすればクラスに落とし込めるよね!
と思ったけどやはり面倒そうである(なんだそりゃ)

2012年11月4日日曜日

knockout.js

javascriptコードを書きながらknockout.jsを学べる素敵サイト
http://learn.knockoutjs.com/#/?tutorial=intro

まだコレクションのところまでしかやってないけど。

プログラムの仕様書って…


プログラムの構文はネットで検索すればかんたんに答えが見つかるけど
仕様書の書き方については決まった答えがないので難しい。
作るプログラムの種類・規模とか、誰に見せるのか、
とかで、必要になる仕様書がまったく変わってくると思う。

仕様書とはどんなものか?
http://japanese.joelonsoftware.com/PainlessSpecs/2.html

正しい、まともな仕様書って何よ(何)