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>
で、このTResult
はTModel
を式ツリーの記述に沿って展開したものであることが期待されています。
Html.DisplayForの書き方をミスっているため、TModel
がIEnumerable<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でこの件に関する記事がなかったので投稿してみました。
誰かの助けになれればうれしいです。