2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MS「ごめん、CS1963って何?」

Last updated at Posted at 2022-10-12

CS1963 "An expression tree may not contain a dynamic operation"

式ツリーに動的な動作を含めることはできません

ぼく「今日も楽しくASP.NET Core!」
ぼく「"式ツリーに動的な動作を含めることはできません" って怒られてコンパイル通らないけど」
ぼく「どんなエラーなんだろう?日本語がふにゃふにゃでよくわからないし、MSに聞くか」
ぼく「Visual Studioなら、エラーをクリックして飛べるから楽ちん!」

MS「Sorry, we don't have specifics on this C# error
MS「うちのフォーラムで聞いて💛」
MS「stack overflowでもいいよ💛」

ぼく「??????????????????」

導入終わり

当然のようにMSのフォーラムではなにも出てこなかったので、
なぜこのようなエラーが発生したのか、どうすれば回避できるのかを調べてみました。
間違い、勘違いなどありましたらコメントで指摘をお願いします🙇

※コメント欄で指摘がありました。そもそもViewBagの使い方を勘違いしているよとのことです。
やっぱり勘違いしているじゃないか!
本題のエラー周りは違わないので変更はしていませんが、このコードみたいなViewBagの使い方は
やめましょう。。。

発生原因

式ツリーの中で動的な何かを使用した場合に発生するエラーです。
式ツリーとは、C#では主にラムダ式などで用いられている、Func型のことです(違うかも)。
以下の私が書いたコードでは、MVCのView部でエラーを吐いています。

@model IEnumerable<HogeProject.Models.Person>

@foreach (var item in ViewBag.SearchResult) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.name) <!-- item.nameでエラーを吐く -->
        </td>
        <!-- 以下ほかのデータが続く -->
    </tr>
}

ここでViewBag.SearchResultは、controllerがmodelをよしなに処理したデータが入っています。
modelから検索条件に一致したデータのみを抽出し、抽出したデータを配列に処理しています。
modelにはnameが含まれているので、私はitem.nameでmodelから該当データのnameを取得できることを期待していました。

ですが、ここでのViewBag.SearchResultはコンパイル時点では型が分かりません。
実際に処理が行われ、controllerから実体が渡されることで初めて型が分かります。
そういう型を、C#ではdynamic型としてコンパイルします。
さらに、元データViewBag.SearchResultがdynamicなので、varで宣言したitemの型もわかりません。
TModelという、何らかのmodelであるという情報のみを持つクラスになってしまいます。
そして、このTModelは動的な型です。

ここで当初のエラー文を思い出してみてください。
式ツリーに動的な動作を含めることはできません
ラムダ式という式ツリーの中で、TModelという動的な型を使用しようとしました。
DisplayForの期待する式ツリーは、Func<TModel, TResult>で、このTResultTModelを式ツリーの記述に沿って展開したものであることが期待されています。
Html.DisplayForの書き方をミスっているため、TModelIEnumerable<HogeProject.Models.Person>だと勘違いされているので、変換がうまく決まりません。
そのため、最後までitemの型がよくわからないままに、コンパイラが解析に失敗します。
式ツリーは動的な型を元に作成できないというルールがあるので、はじめのエラーが返ってきます。

解決方法

このエラーに対する解決法は、varをやめて、型を明示的に宣言してしまうことです。

@model IEnumerable<HogeProject.Models.Person>

@foreach (Person item in ViewBag.SearchResult) { <!-- ここでPersonと宣言 -->
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.name)
        </td>
        <!-- 以下ほかのデータが続く -->
    </tr>
}

すると、itemの型が動的に変更されなくなるので、このエラーは消えます。

このコードに関していえば、以下のように修正することもできます。

@model HogeProject.Models.Person <!-- ここで正しい型を宣言 -->

@foreach (var item in ViewBag.SearchResult) { <!-- ここはvarのまま -->
    <tr>
        <td>
            @Html.DisplayFor(item => item.name) <!-- item => item.nameに変更 -->
        </td>
        <!-- 以下ほかのデータが続く -->
    </tr>
}

DisplayFor内のラムダ式の左辺は、@modelで宣言した型を参照します。
そのため、itemもその型であることが期待されます。
結果として、静的な型扱いをされます。

まとめ

いかがでしたか(テンプレ)?
MSのサイトに行ってもエラー解説がないこと、まれによくあることみたいですね。
今回は、Qiitaでこの件に関する記事がなかったので投稿してみました。
誰かの助けになれればうれしいです。

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?