WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

ASP.NET MVC の View での CS8602 対応

by WebSurfer 1. August 2023 18:24

ASP.NET Core MVC アプリで、以下の画像のように View でナビゲーションプロパティ経由でデータを取得するコードを書くと、プロパティの型が null 許容参照型の場合は "CS8602: null 参照の可能性があるものの逆参照です" という警告が出ます。これにどう対応するかという話を書きます。

null 許容の警告

(ターゲットフレームワークが .NET 6.0 以降のプロジェクトでは全体で「Null 許容」オプションが有効になっています。リバースエンジニアリングを使って生成する Entity Framework 用のコンテキストクラス、エンティティクラスも、基になるデータベースの構造に合わせて、プロパティに null 許容参照型が使われるようになります)

どう対応するかですが、結論から書きますと、何しなくても問題は出なかったです。上の画像のコードで Category, Supplier が null でも NullReferenceException がスローされることはなくアプリは完了し、ブラウザ上ではそれらの項目は空白となります。

以下に、そのあたりのことをもう少し詳しく書きます。

上の画像の View には Controller から IEnumerable<Product> 型の Model が渡されています。そのコードは以下の通りです。Include を使って Category, Supplier も取り込んでいるところに注意してください。

var northwindContext = _context.Products
                       .Include(p => p.Category)
                       .Include(p => p.Supplier);

return View(await northwindContext.ToListAsync());

上の画像で警告が出ているコードの item というのは Product クラスのオブジェクトになります。

この記事の例では、既存の SQL Server サンプルデータベース Northwind の Products, Suppliers, Categories テーブルからリバースエンジニアリングでコンテキストクラスとエンティティクラスを作成して使っており、そのエンティティクラスの一つが Product クラスです。

Product クラスの基になる SQL Server の Products テーブルは下の画像の構成となっています。赤枠で示した SupplierID, CategoryID フィールドは NULL 可で、Suppliers, Categories テーブルに FK 制約が張られています。

SQL Server の Products テーブル

このテーブルからリバースエンジニアリングで生成される Product エンティティクラスの Category, Supplier ナビゲーションプロパティは、以下の通り null 許容参照型となります。

[ForeignKey("CategoryId")]
[InverseProperty("Products")]
public virtual Category? Category { get; set; }

[ForeignKey("SupplierId")]
[InverseProperty("Products")]
public virtual Supplier? Supplier { get; set; }

それゆえ、この記事の一番上の画像のように、View でそれらのナビゲーションプロパティ経由で値を取得しようとすると、"CS8602: null 参照の可能性があるものの逆参照です" という警告が出ます。

Products テーブル の SupplierID, CategoryID フィールドは NULL 可なので、Product エンティティクラスの Category, Supplier ナビゲーションプロパティから取得できる値は null になることがあります。

実際に Products テーブル の SupplierID, CategoryID フィールドを NULL にすると、View のコードで item.Category, item.Supplier は null になります。下の画像がその例です。item の中の Category と Supplier を見てください。

デバッグ画面

ということで、NullReferenceException がスローされないよう対処する必要がある・・・と思っていましたが、実際は、上にも書きましたように、NullReferenceException がスローされることはなくアプリは正常に終了し、ブラウザ上での表示はそれらの項目は空白となります。

ただし、そのあたりのことを書いた Microsoft のドキュメントは見つけることができませんでした。なので、試した結果からそう言っているだけで、どういう状況でも 100% 問題ないかまでは自信がありません。

(Microsoft のドキュメントの「必須のナビゲーション プロパティ」のセクションに "必要な依存が適切に読み込まれている限り (例: 経由 Include)、ナビゲーション プロパティにアクセスすると、常に null 以外が返されます" と書いてありますが、違う話のような気がします)

どうしても対応したいという場合はどうしたらいいでしょうか? Microsoft のドキュメント「null 許容の警告を解決する」に書いてある "変数を逆参照する前に変数が null でないこと確認する" ということになりますが、@Html.DisplayFor の引数のラムダ式上ではそれはできないようです。

ちなみに、三項演算子を使うと "InvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions." というエラーになります。null 合体演算子を使って item.Supplier?.CompanyName ?? "" のようにすると "CS8072 式ツリーのラムダに null 伝搬演算子を含めることはできません" というエラーになります。

というわけで、"変数を逆参照する前に変数が null でないこと確認する" には以下のようにすることになりそうです。

<td>
    @{
        var categoryName = (item.Category != null) ?
            item.Category.CategoryName : "";
    }
    @Html.DisplayFor(modelItem => categoryName)
</td>
<td>
    @{
        var supplierName = (item.Supplier != null) ?
            item.Supplier.CompanyName : "";
    }
    @Html.DisplayFor(modelItem => supplierName)
</td>

そこまでやらなくても、! (null 免除) 演算子を使って警告を消すだけでもよさそうな気はしますが。

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  June 2024  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar