2012年12月13日木曜日

単体テストのこと


自動テストを書くのが最近の目標。だがどういうテストを書けばよいかさっぱり分からない。

VisualStudioのテスト自動生成機能でテストを作りはするものの、一回しか動かさないで放置してることが多々あり。
よくあるのがデータベースにアクセスするメソッドのテストを実行してみたら
テストデータが整っていないせいでテスト失敗して、面倒になって放置というパターン。


「レガシーコード改善ガイド」という本で
データベースとやり取りするようなテストは単体テストではないという記述を見かけた。

理由:単体テストはコードに変更を加えるたびに高速で実行される必要があるため。

なるほど、単体テスト。


テストが容易な ASP.NET MVC アプリケーションを構築する
http://msdn.microsoft.com/ja-jp/magazine/dd942838.aspx

リポジトリパターンでテスト時にモックモデルを突っ込めるようにすれば、DBアクセス無しに単体テストが実行できそう。

とりあえずビジネスロジック系の単体テストをつくることを目標にしようかと思った。
結合テストとかはそれができてから考えていくとして。


関係ないけどレガシーコード改善ガイドに書いてあった
「コードをテストで保護する」という表現が気に入った。早く保護して安心感を得たい。

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

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

2012年10月28日日曜日

[C#] Func デリゲート

ラムダ式を書くときにインテリセンスで見かけるFunc型。
C#で定義済みのデリゲートらしい。

■そもそもデリゲートの定義
http://ufcpp.net/study/csharp/sp_delegate.html#definition

■Funcデリゲート
http://msdn.microsoft.com/ja-jp/library/vstudio/bb549151.aspx


Func<T, TResult>

っていう型の引数があったら

「T型の引数を取ってTResult型の戻り値を返すデリゲート」

を渡せばよい事になる。なんのこっちゃ。


Funcデリゲートを引数に取るメソッドを作ってみる。


        public void Test()
        {
            // ラムダ式で匿名メソッドのデリゲートを渡す。

            // (ここではmsg を引数に取り、それに文字列を足したものを戻り値とする)
            string result1 = funcTest(msg => { return msg + "1"; });
            string result2 =
funcTest(msg => { return msg + "2"; });

            Console.WriteLine(result1);
            Console.WriteLine(result2);
        }

        private string
funcTest(Func<string, string> method)
        {
            return method("ほげほげ");
        }




■実行結果
ほげほげ1
ほげほげ2


なんというか、ぴんとこない。まあいいや。


Func(T1, T2, TResult)だとかFunc(T1, T2, T3, TResult)だとか
引数の数を増やしただけのオーバーロードがたくさんあるようだ。



■よく見かける(ような気がする)定義済みのデリゲート、メモ

Predicate<in T>
T型の引数を取ってboolを返す
http://msdn.microsoft.com/ja-jp/library/vstudio/bfcke1bz.aspx

Action<in T>
T型の引数を取って戻り値を返さない
http://msdn.microsoft.com/ja-jp/library/vstudio/018hxwa8.aspx

VsVim

http://visualstudiogallery.msdn.microsoft.com/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329

VisualStudioにVsVimを入れる → VIでコーディングがはかどる → 最強

※効果には個人差があります

2012年10月20日土曜日

[ASP.NET MVC] WebGrid のページング(2)

参考サイト
http://msdn.microsoft.com/ja-jp/magazine/hh288075.aspx

サーバー側のページ切り替えをやってみるテスト。
表示に必要なデータだけをDBから取得する。そしてWebGridでページングする。

■サーバー側のデータ取得(コントローラー)

        /// <summary>
        /// ページングのテスト
        /// </summary>
        /// <returns></returns>
        public ActionResult Paging(int? page)
        {

            // page は WebGrid が生成するページングリンクのURLに
            // GETパラメータとして指定されている
            return View( new PaginatedList<SchoolViewModel>( 

                              getData() , (page != null ? (int)page - 1: 0) , 3 )
            );
        }

        /// <summary>
        /// データを取得します
        /// </summary>
        /// <returns></returns>
        private IQueryable<SchoolViewModel> getData()
        {
            Models.hogeEntities db = new Models.hogeEntities();
            return
                (from sc in db.SchoolClass
                 join s in db.Schooler
                 on sc.ClassID equals s.ClassID
                 orderby sc.Grade, sc.Class
                 select new SchoolViewModel()
                 {
                     Grade = sc.Grade
                    ,
                     Class = sc.Class
                    ,
                     name = s.Name
                 });
        }



ここ↓のPaginatedListというクラスをそのまま流用している
http://www.atmarkit.co.jp/fdotnet/scottgublog/nerddinner/part08.html

■PaginatedList

    public class PaginatedList<T> : List<T>
    {

        public int PageIndex { get; private set; }
        public int PageSize { get; private set; }
        public int TotalCount { get; private set; }
        public int TotalPages { get; private set; }

        public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            PageSize = pageSize;
            TotalCount = source.Count();
            TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);

            this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
        }

        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 0);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageIndex + 1 < TotalPages);
            }
        }
    }



コンストラクタでIQueryableのデータをSkip&Takeして、PageIndexに該当するデータを取得してる。



■chtml

@model MvcApplication1.Models.PaginatedList<MvcApplication1.Models.SchoolViewModel>

@{
    ViewBag.Title = "Paging";
    Layout = "~/Views/Shared/_Layout.cshtml";

    // WebGridオブジェクト生成
    var grid = new WebGrid(null, rowsPerPage: Model.PageSize);
    grid.Bind(Model, rowCount: Model.TotalCount, autoSortAndPage: false);
}

<h2>Paging</h2>

@* WebGridオブジェクトでレンダリング *@
@grid.GetHtml(columns:grid.Columns(
grid.Column("Class","クラス", canSort:false)
    ,grid.Column("Grade", "学年", canSort:false)
    ,grid.Column("Name", "名前", canSort:false)
    )
)


ポイントっぽいところ
・WebGridのコンストラクタで source にnullを指定する
・Bindメソッドでページの表示に必要なデータと全体の件数を渡し、autoSortAndPage を false にする
(autoSortAndPage を false にするとデフォルトのソート機能も無効になる)






WebGridにページに表示する分のデータしか渡してないけど
ちゃんとページングのリンクを生成してくれている。

2012年10月14日日曜日

[ASP.NET MVC] WebGrid のページング

WebGridはデータを表形式で表示してくれるRazor ヘルパー。
(ASP.NET WebForms の GridView のようなものと理解している)

ページングが簡単にできるのがいいと思った。

chtmlファイルのおれおれサンプル


@model IList<MvcApplication1.Models.SchoolViewModel>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";

    // WebGridオブジェクト生成
    var grid = new WebGrid(source:Model, rowsPerPage: 3);
}

<h2>Index</h2>

@* WebGridオブジェクトでレンダリング *@

@grid.GetHtml(
columns: grid.Columns(
grid.Column("Grade", "学年")
, grid.Column("Class", "クラス")
, grid.Column("Name", "名前")
    )
)


WebGridオブジェクトのコンストラクタに rowsPerPage(1ページに表示するデータ数)を
指定するとページングできるようになる。
WebGridが生成するページングのリンクURLにはGETパラメータが付加されていて
それによって現在のページを把握しているようだ。



※表の見た目等はGetHtml()の引数で変えられる


WebGridにソースとして100件のデータを渡しても
例えば1ページ10件ごとの設定なら当然ながら10件分のデータしか表示されない。

実際ページの表示に使うデータだけをDBから取得して
WebGridでページングを行うにはどうすればよいのか?
下記のサイトにやり方が書いてあるっぽい。
http://msdn.microsoft.com/ja-jp/magazine/hh288075.aspx



でも今日は疲れたのでまた今度やる!(ヘタレ)

Razorって…なに?

Razor 構文とは?
http://msdn.microsoft.com/ja-jp/asp.net/hh182563.aspx

> 従来の ASPX や PHP よりも少ないコード量やタイプ量で軽快に動的な Web ページを記述することができます。

要するにASPXより楽できるんですか。

2012年10月6日土曜日

[C#] コンストラクタ初期化子

コンストラクタってこんな書き方できるんだ…。初期値を指定できるのね。


    public class Hoge
    {
        public string Foo { get; set; }
       
        public Hoge() : this("だれか")
        {
        }

        public Hoge(string foo)
        {
            this.Foo = foo;
        }
    }


WindowsServer2003でW3SVCサービスを再起動せずに仮想ディレクトリの.NET Frameworkのバージョンを変更する方法

長いタイトル。

仮想ディレクトリの.NET Frameworkのバージョンを2→4に上げようとしたら

Framework バージョンを変更するには W3SVC サービスを再起動する必要があります。

などとというメッセージが出た。ええいやってしまえ!と再起動したかったけど、本番サーバーなのでそうもいかず。


W3SVCを再起動せずにバージョンを変更するには aspnet_regiis.exe を使うらしい。

たぶんここ↓で調べた(昔のメモなのでよく覚えてない)
http://stackoverflow.com/questions/1804208/how-do-you-work-out-the-iis-virtual-path-for-an-application


.NET Framework4の aspnet_regiis.exe は下記のフォルダにあった。
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319


■手順

1.-lk オプションでIIS上のパスを調べる。
aspnet_regiis.exe -lk

MSDNによると -lk オプションは

ASP.NET が割り当てられている、すべての IIS メタベース キーのパスとバージョンを一覧表示します。

 フーン( ´_ゝ`)


実行するとこんなの↓が一覧でずらずら出てくる
W3SVC/<数値>/ROOT/<アプリケーション名>/


2.-s と -norestart オプションに 1. で調べたパスをつけて設定完了
aspnet_regiis.exe -norestart -s "W3SVC/<数値>/ROOT/<アプリケーション名>/"

MSDNによると -s オプションは

Aspnet_regiis.exe に関連付けられた ASP.NET ISAPI バージョンをポイントするスクリプト マップを、指定したアプリケーション ルート パスとそのサブディレクトリ内のすべての ASP.NET アプリケーションにインストールします。ASP.NET ISAPI の以前のバージョンを使用する、指定したパス以下にある既存のスクリプト マップは、すべて更新されます。

なるほどよくわからない!

実行したらちゃんとバージョン変ってたのでよしとする!(それでいいのか)

2012年9月30日日曜日

[C#] NLog

http://news.mynavi.jp/articles/2010/03/19/nlog/index.html

ログの出力には log4net を使っていたのだけどこういうのもあったんだー
log4netに比べて導入がかんたん!と書いてある。


NuGet の Package Manager Consoleからインストールした

こんな感じでコマンド打った
http://nuget.org/packages/NLog 

PM> Install-Package NLog

これだけでインストール完了
便利!!



VisualStudioのプロジェクトに、
NLogの設定ファイルであるところの NLog.config ファイルを追加する。


設定ファイル(NLog.config )のおれおれサンプル

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!--(1)出力先(ターゲット)の設定-->
  <targets>
    <!--ファイル(100KByte毎ローテーション)-->
    <target name="file" xsi:type="File" fileName="${basedir}/logs/log.txt"
            layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"
            archiveFileName="${basedir}/logs/${date:format=yyyyMMdd}/log.{###}.txt"
            archiveAboveSize="102400"
            archiveNumbering="Sequence"
            maxArchiveFiles="100" />
  </targets>

  <!--(2)出力ルールの設定-->
  <rules>
    <!-- デバッグレベル以上のログをfileターゲットに出力-->
    <logger name="*" minlevel="Debug" writeTo="file" />
  </rules>

</nlog>




出力のレイアウトは、デフォルトのレイアウトに例外の文字列を足しただけ。


プログラムから出力してみる



    using NLog;

    public class LoggingTest
    {
        // ロガーの生成
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public static void Test()
        {
            logger.Info("Infoメッセージ");
            logger.Warn("Warnメッセージ");
            logger.Error("Errorメッセージ");
            // 他にもいろいろある(適当)

            try
            {
                ExceptionTest();
            }
            catch (Exception ex)
            {
                logger.ErrorException("予期せぬエラーが発生しました", ex);
                // InfoException とか WarnException もあるみたい(適当)
            }
        }

        public static void ExceptionTest()
        {
            try
            {
                throw new Exception("これは内部例外だよ");
            }
            catch (Exception ex)
            {
                throw new Exception("なんかエラー発生!", ex);
            }
        }
    }



例外の詳細をロギングするには ErrorException を使う必要があるようだ。


ログファイルにはこんな感じで書き込まれた。


2012-09-25 18:04:07.9166|INFO|TestApplication1.LoggingTest|Infoメッセージ
2012-09-25 18:04:07.9296|WARN|TestApplication1.LoggingTest|Warnメッセージ
2012-09-25 18:04:07.9296|ERROR|TestApplication1.LoggingTest|Errorメッセージ
2012-09-25 18:04:07.9296|ERROR|TestApplication1.LoggingTest|予期せぬエラーが発生しました System.Exception: なんかエラー発生! ---> System.Exception: これは内部例外だよ
   場所 TestApplication1.LoggingTest.ExceptionTest() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 37
   --- 内部例外スタック トレースの終わり ---
   場所 TestApplication1.LoggingTest.ExceptionTest() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 41
   場所 TestApplication1.LoggingTest.Test() 場所 C:\VisualStudioProjects\TestApplication1\TestApplication1\Logging.cs:行 24



ところで NuGet ってなんて読むの?ニューゲット?エヌユーゲット?ヌゲット?

2012年9月23日日曜日

[ASP.NET MVC] WebAPI で画像を返す

画像ファイルを読み込んで返す。

        // ロギング用
        private static Logger logger = LogManager.GetCurrentClassLogger();

        // ローカル画像を読み込んで返す
        public HttpResponseMessage Get()
        {
            try
            {
                var stream = new FileStream(@"C:\temp\1.png", FileMode.Open);

                var res = new HttpResponseMessage()
                {
                    Content = new StreamContent(stream)
                    ,
                    StatusCode = System.Net.HttpStatusCode.OK
                };
                res.Content.Headers.ContentType 

                    = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
                return res;
            }
            catch (Exception ex)
            {
                logger.ErrorException("画像取得中にエラーが発生しました", ex);
                return new HttpResponseMessage()
                {
                    Content = new StringContent("予期せぬエラーが発生しました。")
                    ,
                    StatusCode = System.Net.HttpStatusCode.InternalServerError
                };
            }
        }




Imageクラスを(バイト配列にして)返す事もできる。
   

        // ローカルの画像ファイルをImageクラスに読み込んで返す
        public HttpResponseMessage Get()
        {
            try
            {
                byte[] bytes = null;

                // 画像をImageクラスに読み込む
                using (Image img = Image.FromFile(@"C:\temp\1.png"))
                using (Graphics g = Graphics.FromImage(img))
                {
                    // 現在時刻とか書き込んでみる

                    g.DrawString("Hello WebAPI."
             , new Font("MS Gothic", 10)
             , Brushes.Black, 10, 80);
                    g.DrawString(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), 

                        new Font("MS Gothic", 10)
           , Brushes.Black, 10, 100);

                    // バイト配列にする
                    ImageConverter imgconv = new ImageConverter();
                    bytes = (byte[])imgconv.ConvertTo(img, typeof(byte[]));
                }

                var res = new HttpResponseMessage()
                {
                    Content = new ByteArrayContent(bytes)                    ,
                    StatusCode = System.Net.HttpStatusCode.OK
                };
                res.Content.Headers.ContentType 

                    = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
                return res;
            }
            catch (Exception ex)
            {
                logger.ErrorException("画像取得中にエラーが発生しました", ex);
                return new HttpResponseMessage()
                {
                    Content = new StringContent("予期せぬエラーが発生しました。")
                    ,
                    StatusCode = System.Net.HttpStatusCode.InternalServerError
                };
            }
        }



ロギングとかエラーの返し方は適当








2012年9月16日日曜日

[C#] LINQ To Entities でコレクションのプロパティに値を突っ込む

なにを言いたいのかいまいちよくわからないタイトル。


コレクションをプロパティに持つクラスがあるとして

    public class SchoolViewModel    {
        public int ClassID { get; set; }
        public int Grade { get; set; }
        public int Class { get; set; }
        public IEnumerable<SchoolerViewModel> SchoolerList { get; set; }
    } 


    // 上のコレクションの要素となるクラス
    public class SchoolerViewModel
    {
        public int ClassID { get; set; }
        public string Name { get; set; }
        public int Number { get; set; }
    }


このにコレクションにLINQでがつんと値を入れる


from school in db.School
   join sc in db.Schooler on school.ClassID equals sc.ClassID into schooler
   select new SchoolViewModel()
   {
       ClassID = school.ClassID
       ,
       Grade = school.Grade
       ,
       Class = school.Class
       ,
       SchoolerList = schooler.Select(
           s => new SchoolerViewModel() { 

               ClassID = s.ClassID, Name = s.Name, Number = s.Number 
           } )
   };
}


LINQの into 句とか select の中で もう一度 select してるあたりがミソ(だと思ってる)。
SQLは一発しか発行されていない。
取得されるデータをLINQさんが勝手にグループ化してコレクションのプロパティに入れてくれるという。LINQさんってすげぇ…。

2012年9月9日日曜日

[C#] IEnumerable と IQueryable

http://www.codeproject.com/Articles/231163/IQueryable-Vs-IEnumerable-in-terms-of-LINQ-to-SQL

LINQの結果をIEnumerableとして取得するかIQueryableとして取得するかで発行されるSQLが変わる…?

よくわからないので自分で試してみる。

SchoolClassなる学校のクラスデータが入っているテーブルと、
それに紐つく Scholler(生徒) テーブルを用意。


まずはIEnumerable

        private IEnumerable<SchoolClass> GetSchoolAsEnumarable()
        {
            return from school in db.SchoolClass

                   where school.Grade > 1
                   select school;
        }

        public void ConsoleOutScholler()
        {

             // クラスデータをIEnumerableとして取得
            IEnumerable<SchoolClass> schoolList = GetSchoolAsEnumarable();

            // IEnumerableとして取得したクラスデータに生徒テーブルを結合 
            var schoolerList =
                   from school in schoolList
                   join schooler in db.Schooler
                   on school.ClassID equals schooler.ClassID
                   where school.Grade > 2

                   select schooler;

            foreach (var schooler in schoolerList)
            {
                Console.WriteLine("出席番号:" + schooler.Number + " 名前:" + schooler.Name);
            }
        }


実際にどのようなSQLが発行されているかトレースしてみると…
(データベースはSQLServer)


■発行されたSQL1
SELECT
[Extent1].[ClassID] AS [ClassID],
[Extent1].[Number] AS [Number],
[Extent1].[Name] AS [Name]
FROM [dbo].[Schooler] AS [Extent1]

■発行されたSQL2
SELECT
[Extent1].[ClassID] AS [ClassID],
[Extent1].[Grade] AS [Grade],
[Extent1].[Class] AS [Class]
FROM [dbo].[SchoolClass] AS [Extent1]
WHERE [Extent1].[Grade] > 1


SQLが2つ発行されてる…遅延実行で最終的に一個のSQLになると思ってたんだけど…。
しかも生徒テーブルの方を先に読み込んでいるのがよくわかない件について。
あと school.Grade > 2  がない。えっ。どうやらそちらの条件は.NETの方で絞っているようだ。


次に IQueryable として受け取ってみる

        private IQueryable<SchoolClass> GetSchoolAsQueryable()
        {
            return from school in db.SchoolClass

                   where school.Grade > 1
                   select school;
        }

        public void
ConsoleOutScholler()
        {

            // クラスデータをIQuaryableとして取得
            IQueryable<SchoolClass> schoolList = GetSchoolAsQueryable();

            // IQuaryableとして取得したクラスデータに生徒テーブルを結合
             var schoolerList =
                  from school in schoolList
                   join schooler in db.Schooler
                   on school.ClassID equals schooler.ClassID
                   where school.Grade > 2

                   select schooler;

            foreach (var schooler in schoolerList)
            {
                Console.WriteLine("出席番号:" + schooler.Number + " 名前:" + schooler.Name);
            }
        }


■発行されたSQL
SELECT
[Extent2].[ClassID] AS [ClassID],
[Extent2].[Number] AS [Number],
[Extent2].[Name] AS [Name]
FROM  [dbo].[SchoolClass] AS [Extent1]
INNER JOIN [dbo].[Schooler] AS [Extent2] ON [Extent1].[ClassID] = [Extent2].[ClassID]
WHERE ([Extent1].[Grade] > 1) AND ([Extent1].[Grade] > 2)


SQLが一つになったよ!やったね!
で、なんで IEnumerable のときは2つ発行されてたの?


発行されるSQLが違う理由についてはこのサイト↓にIEnumerableは「pull型」、IQueryableは「外部クエリ」という説明があった。
http://www.atmarkit.co.jp/fdotnet/chushin/roadtolinq_01/roadtolinq_01_03.html

> IEnumerable<T>インターフェイスを使っても外部システムからの
> データ取得はできなくはないのだが、データを全件返すという部分がネックになる

そうなの…。

とにかく LINQ to SQL と LINQ to Entities のときは素直に IQueryable として使いますね?(それでいいのか)

2012年9月2日日曜日

[C#] LINQ to Entities で数値を文字列に変換

LINQ to Entities の話し。

LINQで数値の列を ToString() で文字列にしようとしたらプログラム実行時にエラーになった。


NotSupportedException: メソッド 'System.String ToString()' は LINQ to Entities では認識されないため、ストア式に変換できません。



ToString()をSQLに変換する方法知らないんです!できません!と言っているようだ。
知らないなら実行時じゃなくてもっと早い段階で教えてくださいっ、とか思うけどそれはまあ難しいのだろう。

じゃあどうすればいいのという事で調べてみると StringConvert というメソッドが見つかった。
http://msdn.microsoft.com/ja-jp/library/vstudio/dd466166


LINQで使ってみる


from hoge in db.HogeTable
select SqlFunctions.StringConvert((double?)hoge.Num)



StringConvertの部分がどのようなSQLに変換されているかLINQPadで確認。
データベースはSQLServer。


SELECT
STR( CAST( [Extent1].[Num] AS float)) AS [C1]
FROM [dbo].[HogeTable] AS [Extent1]



確かに文字列に変換しているようだった(当たり前)。

2012年8月26日日曜日

[ソース管理] VisualStudio2010+TeamFoundationServer2012 でチームプロジェクトが作成できない罠

Team Foundation Server 2012をインストールしたサーバーに対して、
Visual Studio 2010からチームプロジェクトを作ろうとしたら、

「TFS30172: 現在のユーザーには新しいチームプロジェクトを作成するのに十分なアクセス許可がありません。」

的なメッセージが。

おかしい…権限はあるはずなのに…。

エラーメッセージで検索したら以下のブログが見つかった。
http://noblevelop.com/tf30172-you-do-not-have-permission-to-create-a-new-project/
http://blogs.msdn.com/b/buckh/archive/2012/03/05/permission-error-with-creating-a-team-project-from-vs-2010-on-tfs-11-beta.aspx

不具合?かどうかは知らないけどとにかくできないらしい。しかも権限の問題ではないらしい…。


Visual Studio 2012だとチームプロジェクトを作成する事ができるので、
VS2012でチームプロジェクトを作成しておいてそれをVS2010から利用することはできるみたい(なんてまわりくどい使い方)。

そのうちアップデートで修正されるのだろうか。

2012年8月19日日曜日

[C#] LINQでXMLの内容をクラスにぶち込む

XMLをクラスに入れて扱いやすくしたかった。


LINQ to XML についての参考サイト
http://code.msdn.microsoft.com/windowsdesktop/22-LINQ-to-XML-XML-c2f6f04f

上のサイトのコード例を少し変えてクラスにぶち込むようにした。


こういうクラスを用意して

public class Person
{
    public string ID { get; set; }
    public string Name { get; set; }
}


XMLの要素をクラスにぶち込む

// XML文字列
string xmldoc =
  @"<?xml version='1.0'?>
<names xmlns='http://www.piedey.co.jp/example/linqtoxml201003'>
  <name id='M0001'>一郎</name>
  <name id='M0002'>次郎</name>
  <name id='F0001'>花子</name>
</names>
";

// XElementを取得. XElementにするとLINQで扱えるようになる
// (名前空間も System.Xml.Linq.XElement となってる)
var doc = XElement.Parse(xmldoc);

// 名前空間
XNamespace ex =
    "http://www.piedey.co.jp/example/linqtoxml201003";

// LINQでクラスオブジェクトに入れる
IEnumerable<Person> persons =
    from n in doc.Elements(ex + "name")
    select new Person()
    {
     ID = n.Attribute("id").Value
     ,
     Name = n.Value
    };


XmlDocumentにして要素をくるくる回して解析してた事を考えると
超楽ちん!うっひょー!