WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ASP.NET MVC の View での CS8602 対応

by WebSurfer 2023年8月1日 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

.NET 6.0 ASP.NET Identity に MySQL 使用 (その2)

by WebSurfer 2023年2月12日 16:15

.NET 6.0 の ASP.NET MVC アプリで、プロバイダに Oracle 製 MySql.EntityFrameworkCore v6.0.10 を使って、ASP.NET Core Identity のユーザー情報のストアに MySQL を利用する話を書きます。

ASP.NET Identity に MySQL 使用

先の記事「.NET 6.0 ASP.NET Identity に MySQL 使用 (CORE)」では、その時点で Oracle 製 MySql.EntityFrameworkCore は v5.0.8 までしかリリースされていなかったので、プロバイダには Pomelo.EntityFrameworkCore.MySql を使用しています。

その後 Oracle 製 MySql.EntityFrameworkCore の .NET 6.0 用がリリースされたので試してみましたが、v6.0.7 までは Add-Migration に失敗して使えませんでした。

先日バージョンアップの状��を調べてみたら v6.0.10 がリリースされていたので試してみたら Add-Migration は成功するようになってました。その後 Update-Database でデータベースは生成され、Resister でのユーザー登録、Login でのユーザー認証も問題なくできます。(ただし、100% 完全に動くかどうかまでは未検証ですが)

と言うわけで、プロバイダに Oracle 製 MySql.EntityFrameworkCore v6.0.10 を使ってのアプリの作り方を、先の記事の Pomelo.EntityFrameworkCore.MySql と違う点だけ以下に書いておきます。

(1) NuGet パッケージ

NuGet で MySql.EntityFrameworkCore v6.0.10 をインストールします。他に必要なものとバージョンを合わせました。

MySql.EntityFrameworkCore

(2) Program.cs の修正

自動生成されれた Program.cs ファイルで、サービス登録のコードが SQL Server を使うように設定されているはずですが、これを MySQL を使うように変更します。以下のような感じです。

Program.cs の修正

(3) Add-Migration / Update-Database の実行

パッケージマネージャーコンソールから Add-Migration CreateIdentitySchema を実行します (CreateIdentitySchema という名前は任意です)。

xxxxx_CreateIdentitySchema.cs

Migrations と言う名前のフォルダとその中に xxxxx_CreateIdentitySchema.cs というファイル (xxxxx は作成時のタイムスタンプ) が生成されているはずですので確認してください。内容は Pomelo.EntityFrameworkCore.MySql を使った場合と若干異なります。

昔使った MySql.Data.EntityFrameworkCore(今は非推奨)で主キーの長さが指定されてなくて問題となったところは、Pomelo.EntityFrameworkCore.MySql と場合と同様に、varchar(255) に指定されています。

その後、Update-Database を実行すれば接続文字列で指定した名前で MySQL データベースが生成され、その中に ASP.NET Core Identity に必要なテーブルが一式生成されます。

MySQL データベース

Visual Studio から MVC アプリを起動し Register 画面からユーザー登録ができることを確認してください。登録したユーザーは上の MySQL データーベースに反映され、Login 画面から登録した ID とパスワードでログインできるようになります。

この記事の一番上の画像はログインした後で Manage 画面を開いたものです。

Tags: , , ,

CORE

Model Binding Error Messages の差替 (CORE)

by WebSurfer 2023年1月27日 12:35

先の記事「整数型プロパティの検証、エラーメッセージ (CORE)」の一番下に Model Binding Error Messages をローカライズする方法別途検証してみますと書きました。ローカライズと言うよりは、単に英語のエラーメッセージを日本語に差し替えるだけですが、やってみましたので以下にその方法を述べます。

Model Binding Error Messages 差替

.NET Framework の MVC5 アプリの場合は、先の記事「MVC5 でエラーメッセージが英語」に書いたように、NuGet から日本語のサテライトアセンブリ System.Web.Mvc.resources.dll をインストールするという手段がありますが、ASP.NET Core MVC ではそのような解決策は見つかりません。

自力でコードを書いてローカライズする方法は Stackoverflow の記事 ASP.NET Core Model Binding Error Messages Localization 他に見つかりましたのでそれを参考にしました。

だた、見つけた記事に書いてある方法は、多言語対応のため複数のリソースファイルを作り、それからカルチャに応じてその言語のエラーメッセージの文字列をリソースファイルから取得して、デフォルトのエラーメッセージを書き換えるという少々複雑なことを行っています。

先の記事のエラーメッセージ「The value '2000x' is not valid for 価格2 (int).」と「The value '' is invalid.」を日本語化するだけならコードはかなり簡単になります。リソースファイルを作ってそこからメッセージを取得するということは必要ありません。

それにはどうすればよいかと言うと、Program.cs (.NET 5.0 以前は Startup.cs) の AddControllersWithViews メソッドで DefaultModelBindingMessageProvider を取得し、SetAttemptedValueIsInvalidAccessor, SetValueMustNotBeNullAccessor メソッドを使ってデフォルトのエラーメッセージを差し替えてやります。

具体例は下のコードを見てください。Visual Studio 2022 のテンプレートで作った .NET 7.0 の ASP.NET Core MVC の場合のサンプルです。

builder.Services.AddControllersWithViews(options => { 
    DefaultModelBindingMessageProvider provider = 
        options.ModelBindingMessageProvider;
    provider.SetAttemptedValueIsInvalidAccessor(
        (s1, s2) => $"値 '{s1}' は {s2} に対して無効です。");
    provider.SetValueMustNotBeNullAccessor(
        (s) => $"値 '{s}' は無効です。");
});

上のコードにより、先の記事のデフォルトの英語のエラーメッセージ「The value '2000x' is not valid for 価格2 (int).」と「The value '' is invalid.」がそれぞれ「値 '2000x' は 価格2 (int) に対して無効です。」と「値 '' は無効です。」に書き換えられます。この記事の上の画像がその結果です。

デフォルトの Model Binding Error Messages には、上の AttemptedValueIsInvalid, ValueMustNotBeNull を含めて全部で 11 種類あります。

以下に一覧表を載せておきます。それぞれに SetXxxxxAccessor (Xxxxx は名前) というメソッドが用意されていて、それによりデフォルトのエラーメッセージを差し替えることができます。

名前 デフォルト 原因
AttemptedValueIsInvalid The value '{0}' is not valid for {1}. Exception is of type FormatException or OverflowException, value is known, and error is associated with a property.
ValueMustNotBeNull The value '{0}' is invalid. a null value is bound to a non-Nullable property.
MissingBindRequiredValue A value for the '{0}' parameter or property was not provided. a property with an associated BindRequiredAttribute is not bound.
MissingKeyOrValue A value is required. either the key or the value of a KeyValuePair<Key,TValue> is bound but not both.
MissingRequestBodyRequiredValue A non-empty request body is required. no value is provided for the request body, but a value is required.
NonPropertyAttemptedValueIsInvalid The value '{0}' is not valid. Exception is of type FormatException or OverflowException, value is known, and error is associated with a collection element or parameter.
NonPropertyUnknownValueIsInvalid The supplied value is invalid. Exception is of type FormatException or OverflowException, value is unknown, and error is associated with a collection element or parameter.
NonPropertyValueMustBeANumber The field must be a number. Error message HTML and tag helpers add for client-side validation of numeric formats. Visible in the browser if the field for a float (for example) collection element or action parameter does not have a correctly-formatted value.
UnknownValueIsInvalid The supplied value is invalid for {0}. Exception is of type FormatException or OverflowException, value is unknown, and error is associated with a property.
ValueIsInvalid The value '{0}' is invalid. Fallback error message HTML and tag helpers display when a property is invalid but the ModelErrors have nullErrorMessages.
ValueMustBeANumber The field {0} must be a number. Error message HTML and tag helpers add for client-side validation of numeric formats. Visible in the browser if the field for a float (for example) property does not have a correctly-formatted value.

Tags: , , ,

Validation

About this blog

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

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar