LINQ to Entities では ToString が使えない

Entity Data ModelとLINQ to SQL は同時に使えない | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1688

前回の続きということで、Entity Data Model の使いどころ、というか私が陥った落とし穴を紹介します。

さて実は、ASP.NET MVC の連載記事を書いている途中で、Html.DropDownList メソッドを使いたかったのですね。連載記事のほうは、Entity Data Model を使うようにしているので、DropDownList に連結するならば、やっぱり「データバインド」ですね、って形にしたかったのです。

以前の ASP.NET コントロールであれば、

dropDownList.DataSource = items ;

みたいな形でデータソースを指定するわけですが、HTML ヘルパーメソッドの場合は、

Html.DropDownList( “states” );

のように ViewData を使ってバインドさせる方法もあれば、

Html.DropDownList( “states”, Model.items );

のようにモデルのパラメータを使って設定する方法もあります。

そこで都道府県コードをバインドしようかなぁ、と思って、都道府県のテーブルを作成。

create table TStates (
	id int, -- 都道府県コード
	name varchar(20) -- 都道府県名
)

そして、コントローラーに以下のコードを作成。

public ActionResult StatesList()
{
    Models.mvcdbEntities ent = new Models.mvcdbEntities();

    var items = from t in ent.TStates
                select new SelectListItem
                {
                    Value = t.id.ToString(),
                    Text = t.name
                };
    ViewData["states"] = items;

    return View();
}

DropDownList は、SelectListItem のコレクションしか受け付けないので、あらたに new して追加します。データベースから検索できたほうがいいので、LINQ が便利ですよね。って訳で、LINQ を使っています。
SelectListItem は、Text は表示文字列、Value は値なんですがブラウザなので ToString で変換しています(ここが落とし穴です)。

さて、これを動かすと、

▼画像001

20110121_01.jpg

ってなエラーがでます。

System.NotSupportedException はユーザー コードによってハンドルされませんでした。
  Message=メソッド ‘System.String ToString()’ は LINQ to Entities では認識されないため、ストア式に変換できません。
  Source=System.Data.Entity

なんだか良くわかりませんが、ToString は動きません…ってな具合です。

どうやら、「LINQ to Entities」ってのが動いているようで、仕方がない「LINQ to SQL」を動かそう。という訳で、他の方法で試してみます(これを同じアプリケーションに組み込もうとして、前回の落とし穴なんですなw)。

public ActionResult StatesList()
{
    DataContext dc = getDataContext();
    var items = from t in dc.GetTable<TStates>()
                select new SelectListItem
                {
                    Value = t.id.ToString(),
                    Text = t.name
                };

    ViewData["states"] = items;


    return View();
}

のように、LINQ to SQL のほうを動かすとですね。すんなり動きます。う~む。

▼画像002

20110121_02.jpg

何処が引っ掛かっているのかというと、ToString なんですね。どうやら、LINQ to Entities の中では、この手の他のメソッドが動かないらしい。しかも、LINQ の実行が遅延されるので(LINQを定義した箇所ではなくて実際に実行される場所という意味で)、aspx 内でエラーが発生するという不思議な具合になります。

いずれ、LINQ to SQL が LINQ to Entities に全て置き換わるのか…は不明なのですが、こんな風に LINQ 内で文字列処理をしようとするとちょっと不思議な現象に出会います。

なので、LINQ to Entities を使っている場合は、

public ActionResult StatesList2()
{
    Models.mvcdbEntities ent = new Models.mvcdbEntities();
    List<SelectListItem> items = new List<SelectListItem>();
    foreach (var it in ent.TStates)
    {
        items.Add(new SelectListItem
        {
            Value = it.id.ToString(),
            Text = it.name
        });
    }
    ViewData["states"] = items;

    return View();
}

のように、一度 コレクションに代入しないといけないという…ダサい感じに…てなわけで連載のほうでは却下しました(苦笑)。このあたりの説明が主目的ではないので orz

さて、この現象ですが、実は Visual Basic 2010 では起こりません。

Function StatesList() As ActionResult
    Dim ent = New mvcdbEntities
    Dim items = From t In ent.TStates
                Select New SelectListItem With {
                    .Value = t.id,
                    .Text = t.name
                }

    ViewData("states") = items

    Return View()
End Function

のように VB を使って書くとすんなり動きます。

これは、ToString 等の変換を使っていないからなんですね。.Value = t.id のところで、自動的に Integer から String に変換されているわけで、これがうまく動く要因なのです。C# の場合は、自動変換が効かないから ToString メソッドを使うところでアウト、なんですなぁ。

というわけで、同じ LINQ を使っている場合でも、Entity Data Model(LINQ to Entities)を使っている場合と、LINQ to SQL を使っている場合とで区別しないといけないとう、ちょっと不思議な落とし穴(実装が違うといれば、わかりやすいんですが、コード的には LINQ だけなので見つけづらい)。

カテゴリー: ASP.NET パーマリンク