WebSurfer's Home

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

Visual Studio 2022 の ASP.NET MVC アプリ

by WebSurfer 2021年11月22日 16:28

2021 年 11 月 8 日にリリースされた Visual Studio Community 2022 を自分の PC にダウンロードして使えるように設定しました。

Visual Studio Community 2022

まず手始めに、テンプレートを使って .NET 6.0 の ASP.NET Core MVC アプリのプロジェクトを作ってみましたので、Visual Studio 2019 との違いなど気が付いた点を備忘録として書いておきます。

注: ここに書いたことは自分が最初にプロジェクトを作ったときに気が付いた点のみで他にも変更点は多々あるようです。詳しくは Microsoft のドキュメント「ASP.NET Core 5.0 から 6.0 への移行」を見てください。

新しいプロジェクトの作成

この記事を書いた時点での Visual Studio のバージョンは 17.0.1、.NET SDK のバージョンは 6.0.100、.NET Runtime のバージョンは 6.0.0 です。

(1) 開発サーバー

デフォルトで Kestrel をエッジサーバーとして使うようになってました。

開発サーバー

なので、その設定のまま Visual Studio からアプリを起動すると以下のように dotnet run コマンドによってアプリが Kestrel で実行され、

dotnet コマンド

処理された応答が自動的にブラウザに表示されます。(注: 自動的にブラウザに表示されるのは Visual Studio を使った場合です)

ちなみに、Visual Studio 2019 ではデフォルトでは IIS Express を使用するインプロセスホスティングモデルになります。

詳しくは先の記事「開発環境で Kestrel 利用 (CORE)」を見てください。

(2) Bootstrap

Bootstrap.css, Bootstrap.js のバージョンが 5.1.0 にアップグレードされています。

ちなみに、Visual Studio 2019 で .NET 5.0 の ASP.NET Core MVC アプリを作った場合のバージョンは 4.3.1 になります。

自分が気が付いた限りですが、特に大きく違うのがラベルとテキストボックスの表示でした。他にも違いは多々あると思いますが。

Login 画面

他に、Bootstrap.js が jQuery に依存しなくなったことも大きな違いだと思います。

(3) Startup.cs / Program.cs

Startup.cs が無くなっていました。Visual Studio 2019 ではそれにサービス、ミドルウェア追加のためのコードを書いていたのですが、それらは Program.cs に移すことにしたらしいです。

Program.cs にエントリーポイントとして存在しなければならないはずの Main メソッドが見当たりません。Microsoft の「新しい C# テンプレートで、最上位レベルのステートメントが生成される」はコンソールアプリのドキュメントですが、ここに書いてあることが ASP.NET Core MVC にも適用されているようです。

(4) NULL 許容参照型

NULL 許容参照型がプロジェクト全体で有効化されています。

NULL 許容参照型

ASP.NET Core Identity 関係の .cshtml ファイルのソースコードにそれに対応してない部分があって警告が出ます。(.cshtml.cs ファイルの方は #nullable disable の付与で対応済み)

自分が試した時には Logout.cshtml, Login.cshtml, Register.cshtml, _Layout.cshtml の 4 つのファイルで警告が出ました。

Layout.cshtml は (string) ⇒ (string?) に変更、Logout.cshtml は if (User.Identity?.IsAuthenticated ?? false) に変更、Login.cshtml と Register.cshtml は @foreach (var provider in Model.ExternalLogins!) というように null 免除演算子 ! の追加で対処できます。

(5) CSS 分離

.NET 5.0 の Blazor アプリで導入された CSS 分離のための仕組みが MVC アプリにも導入されました。下の画像は Views/Shared/_Layout.cshtml の一部ですが、赤枠で囲ったコードがその機能を使うためのものです。

CSS 分離

Microsoft のドキュメント「ASP.NET Core Blazor の CSS の分離」によると、CSS スタイルを個々のビューに分離して、次のことを回避するのが目的だそうです。

  • 維持が困難なグローバル スタイルへの依存関係
  • 入れ子になったコンテンツでのスタイルの競合

具体的には、以下のようにビューと同じ名前の css ファイルを作成し、

CSS ファイル

このセクションの一番上の画像のように <link ... href="~/<プロジェクト名>.styles.css" ... という外部 css ファイルへの参照をコードに含めると、それからダウンロードされる css ファイルには以下のように属性セレクタが含まれるようになり、

CSS の属性セレクタ

当該ビュー(上の例では _Layout.cshtml)の html 要素には上の css の属性セレクタに設定された属性が付与されます。

html 要素の属性

その結果、css スタイルを個々のビューに分離することができます。

(6) ブラウザーリンクとファイルウォッチャー

html ソースを見ると以下のコードが自動的に(勝手に)生成されています。

ブラウザーリンクとファイルウォッチャー

前者は名前からすると Visual Studio 2015 にもあったブラウザーリンクという機能らしいです。Visual Studio 2019/2022 ではデフォルトで無効になっています。なのにスクリプトファイルを参照しているのは何故か、どのように使うのかは不明・未確認です。

後者はファイルウォッチャートという機能らしいです。aspnetcore-browser-refresh.js で検索すると「ファイル ウォッチャーを使用した ASP.NET Core アプリの開発」という記事が見つかります。

その記事の「ブラウザーの更新」のセクションに "開発中のこのような場合には、アプリに手動でスクリプトを挿入します。 たとえば、Web アプリを構成してスクリプトを手動で挿入するには、_framework/aspnet-browser-refresh.js を含むようにレイアウト ファイルを更新します。" と書いてあります。

Visual Studio 2022 で提供されたホットリロードというデバッグ中にコードを変更しながら開発を行うことを可能にした機能を実現するために使うものらしいですが、詳細は調べ切れてなくて不明です。

これらをどのように利用できるかは今後の課題ということで・・・

Tags: , ,

CORE

MySQL で Contoso University チュートリアル (CORE)

by WebSurfer 2021年10月19日 12:22

データベースに MySQL を利用して、ASP.NET Core MVC アプリを Micorsoft のチュートリアル「ASP.NET Core MVC と EF Core - チュートリアル シリーズ」に従って作成してみました。その際に気になったこと、憶えておいた方が良さそうなことを備忘録として残しておきます。

Contoso University アプリ

チュートリアルはデータベースに SQL Server (LocalDB) を使うことを前提に書かれています。それを MySQL に代えて、チュートリアルの「1. 開始するには」から「8. コンカレンシーの競合の処理」までの手順に従って実装しました。

この記事で使った MySQL サーバーは Windows 10 にインストールしたバージョン 8.0.19 です。詳しくは先の記事「MySQL をインストールしました(その 3)」を見てください。

以下に「1. 開始するには」から「8. コンカレンシーの競合の処理」の各手順において、気になったこと、憶えておいた方が良さそうなことを書いておきます。

一番気になっていたのは「8. コンカレンシーの競合の処理」の手順の楽観的同時実行制御の実装でしたが、モデルのプロパティの定義だけ変更すれば MySQL でも可能でした。その結果が上の画像です。

(1) 開始するには

この記事では、チュートリアルに書いてあるようなプロジェクトの新規作成はせず、先の記事「MySQL で Movie チュートリアル (CORE)」で作成した既存のプロジェクトをベースに使いました。

まず Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore を NuGet からインストールします。その他の必要な NuGet パッケージはベースに使った既存のプロジェクトにインストール済です。結果は以下のようになります。

NuGet パッケージ

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore の役割は "ASP.NET Core middleware for Entity Framework Core error pages. Use this middleware to detect and diagnose errors with Entity Framework Core migrations." だそうです。チュートリアルの「データベース例外フィルターを追加する」と関係があるようです (未確認ですが)。

Strtup.cs ファイルにある Startup クラスの ConfigureServices メソッドに SchoolContext を登録するコードを追加します。以下の通りです。

SchoolContext を登録

チュートリアルの手順「SchoolContext を登録する」に書いてあるコードは SQL Server 用で、これを MySQL 用に変更する必要がありますので注意してください。

次に appsettings.json ファイルに接続文字列を設定します。チュートリアルの例は LocalDB 用ですので MySQL 用に変更します。以下のような感じです (あくまで「感じ」です。MySQL の設定により異なります)。

接続文字列を設定

チュートリアルの「コントローラーとビューを作成する」の手順でスキャフォールディングを行う際、NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になるケースがありますので注意してください。エンティティクラスの定義によると思われます。ここ「(1) 開始するには」の手順では不要でしたが、下の「(6) 関連データの読み取り」では必要でした。

アプリを実行すると EF Code First の機能を使って、接続文字列で database=ContosoUniversity1 と指定した通り ContosoUniversity1 という名前のデータベースと、SchoolContext クラスで指定した Course, Enrollment, Student という名前のテーブルが生成されます。

さらにチュートリアルには初期データをシードするためのコードが含まれており、その通り実装すれば、最初にアプリを実行した時にコードに書かれた通り初期データが Course, Enrollment, Student テーブルに登録されます。

(2) 作成、読み取り、更新、削除の操作

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(3) 並べ替え、フィルター、ページング、グループ化

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(4) 移行

ここは Migration 操作を学ぶことを目的としているようです。

チュートリアルでは dotnet コマンドを使っていますが、Visual Studio を使っているならそのパッケージマネージャーコンソールを使った方が簡単だと思います。

NuGet パッケージ Microsoft.EntityFrameworkCore.Tool が必要ですが、上のステップ (1) の画像の通りインストール済みですので、このチュートリアルの操作にはパッケージマネージャーコンソールを使いました。

Drop-Database, Add-Migration, Update-Database という操作を行いますが、データベースが MySQL だから何か問題が出るということは、少なくともこのチュートリアルの操作ではありませんでした。(この後のセクション「(5) 複合データ モデルの作成」では 2 点問題がありましたが)

ベースに使った既存のプロジェクトには他の既存のコンテキストクラスが含まれています。そういう場合は、Drop-Database, Add-Migration, Update-Database 操作の際に、既存のコンテキストと区別するため、-Context SchoolContext オプションの追加が必要です。

(5) 複合データ モデルの作成

MySQL ではチュートリアルの通りにはできないということがありました。問題があったのは以下の 2 点です。

  1. Column 属性」のセクションで Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性。
  2. Department エンティティを作成する」のセクションで Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性。

いずれも Add-Migration の後の Update-Database コマンドで前者は NotImplementedException、後者は MySqlException がスローされ、MySQL のテーブルの変更・生成に失敗します。

前者は既存の Student テーブルの FirstMidName 列の名前を Migration 操作で FirstName という名前に変更しようというものです。NotImplementedException 例外がスローされるということは MySQL 用の Entity Framework にその実装がされてないということのようです。

後者は MySQL には TypeName = "money" で指定した money 型はないためのエラーです。

上記の操作に失敗で先に進めることができなくなったしまったので、やむを得ずゼロから作り直しました。Drop-Database で ContosoUniversity3 をドロップし、Add-Migration で生成された Migrations フォルダのファイルを削除し、チュートリアルに従ってコードの修正・追加を最後まで終わらせてから(途中で Migration 操作は行わないで)、最後に一気に Add-Migration, Update-Database コマンドでデータベースを生成しました。

問題となった Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性ですが、データベースを生成する最初の時点で付与しておけば指定した通り Student テーブルの当該列の名前は FirstName になります。チュートリアルのように最初に FirstMidName という列名で作って、後で Migration 操作で変更しようとすると、その操作に必要なメソッド等が実装されてないからか、NotImplementedException がスローされます。

Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性は [Column(TypeName = "decimal")] に変更が必要です。

チュートリアルの「移行を追加する」の手順に、Migrations フォルダに生成された ComplexDataModel クラスのコードに手を加えるように書かれていますが、それは先の操作でデータベースに作成済みの既存のレコードとの整合を取るためのもので、ゼロから一気に作るのであれば不要です。

(6) 関連データの読み取り

スキャフォールディング操作の際 NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になります。ちなみに「(1) 開始するには」の手順では不要でした。上の「(5) 複合データ モデルの作成」でいろいろ変更・追加したエンティティクラスの定義によると思われます。

そのため、最初のスキャフォールディング操作には失敗しますが、その際 Microsoft.EntityFrameworkCore.SqlServer が自動的に追加されますので、以下のようになっていることを確認し再度スキャフォールディングすれば成功すると思います。

NuGet パッケージの確認

上の画像の 1 番目から 4 番目のパッケージ間でのバージョンの不一致、ランタイムとのバージョンの不一致があるとスキャフォールディングに失敗することがありますので注意してください。

もう一つ、MySQL とは関係ないチュートリアルのミスですが、Courses Taught by Selected Instructor テーブルを表示する View のコードで selectedRow = "success"; となっていますが、selectedRow = "table-success"; にしないと Select しても背景色が変わらないので修正してください。

(7) 関連データの更新

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(8) コンカレンシーの競合の処理

チュートリアルの手順では Department テーブルに rowversion (Transact-SQL) 列を追加し、それを使って楽観的同時実行制御を実装するというものです。

MySQL には rowversion はなく、それに代わるものとして timestamp というものがありますが同じではありません。.NET 側では rowversion は byte[] 型、timestamp は DateTime 型になるのですが、そのような違いがあっても Entity Framework による楽観的同時実行制御に使えるかが懸念したところです。

チュートリアルには Department クラスに byte[] 型の RowVersion プロパティを追加して Migration 操作でデータベースの Department テーブルに RowVersion 列を追加するよう書かれています。まずそこを MySQL ように変更する必要があります。

ググって見つけた記事 entity framework 6 mysql rowversion を参考に以下のプロパティ定義を使いました。

[Timestamp]
[ConcurrencyCheck]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime TimeStamp { get; set; }

timestamp では分解能が不十分という話もあるようですが、11.3.1 DATE、DATETIME、および TIMESTAMP 型によると "DATETIME または TIMESTAMP 値には、マイクロ秒 (6 桁) までの精度で後続の小数秒部分を含めることができます" とのことですので十分ではないかと思います。なので今回は上のプロパティ定義で実装してみました。

マイクロ秒以内で同時実行が行われるケースも考えなければならないとか、どうしても分解能が気になるという場合は Better way to implement a row version with EF Core and MySQL? に紹介されている方法もありそうです。

Add-Migration, Update-Database コマンドで MySQL の Department テーブルには以下のようにそれらしい形で datetime 型の Timestamp 列が追加されます。timestamp 型でないのがちょっと気になりますが。

Department テーブル

特に気になっていたのはスキャフォールディング機能を使って Department テーブルの CRUD 用のコントローラーとビューを生成したとき TimeStamp 列を使って同時実行制御を行う機能が含まれるかということです。

結果は、SQL Server の場合と同様に、同時実行で DbUpdateConcurrencyException 例外がスローされ楽観的同時実行制御ができるようになりました。この記事の一番上の画像がその結果です。

なお、プロパティ名をチュートリアルの RowVersion から TimeStamp に変えましたので、チュートリアルの「Edit メソッドを更新する」の Edit アクションメソッドのコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。型の指定も byte[] となっているところを DateTime に変更する必要があります。

Edit 用の View のコードも同様で、「Edit ビューを更新する」のコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。

Delete については View の <input type="hidden" asp-for="RowVersion" /> の RowVersion を TimeStamp に変更します。


チュートリアルにはこの先に「9. 継承」と「10. 高度なトピック」がありますが、それはまた次の機会にということで・・・

Tags: , , , ,

CORE

MVC の DropDownList での NULL の処置

by WebSurfer 2021年10月17日 13:31

ASP.NET MVC アプリでデータベースの CRUD 操作を行う際ドロップダウンリストを利用してユーザー入力に便宜を図ることがあると思います。その際、NULL 可になっている列にドロップダウンリストから NULL を入力できるようにするにはどうするかという話を書きます。

ドロップダウンリスト

この記事は ASP.NET MVC5 および ASP.NET Core MVC の場合です。ASP.NET Web Forms アプリの場合は別の記事「DropDownList での NULL の処置」に書きましたのでそちらを見てください。

先の記事「スキャフォールディング機能」および「スキャフォールディング機能 (CORE)」で作ったアプリのドロップダウンリストから NULL を入力できるようにしてみます。

そのアプリは Northwind サンプルデータベースの Products, Suppliers, Categories テーブルから作ったコンテキストクラス、エンティティクラスをベースに、Visual Studio のスキャフォールディング機能を使って CRUD 操作を行うための Controller / View のコードを一式自動生成したものです。

自動生成されたコードの Create, Edit 画面では CatrgoryID, SupplierID 列にドロップダウンリストを使用して CategoryName, CompanyName (名前) が表示され、ID (数字) ではなく名前を見て選択できるようになっています。上の画像を見てください。

Products テーブルの SupplierID, CategoryID 列は NULL 可なのですが、自動生成されたコードでは NULL を選択することはできません。それを、Create, Edit 操作の際ドロップダウンリストから NULL を選択して、その結果を Products テーブルに反映できるようにします。

必要なコードの追加・変更はわずかで、自動生成されている View のコードの一部に以下の修正を行うだけです。

ASP.NET MVC5

View に自動生成されている DropDownList メソッドの第 3 引数として "NULL" という文字列を追加。

@Html.DropDownList("SupplierID", null, "NULL", 
    htmlAttributes: new { @class = "form-control" })

ASP.NET Core MVC

View に自動生成されている select タグヘルパーに以下のように option 要素を追加。

<select asp-for="SupplierID" class="form-control" 
    asp-items="ViewBag.SupplierID">
    <option value="">NULL</option>
</select>

上記の設定の結果、ブラウザに送信される html ソースは以下のようになります。value="" で NULL という項目が追加されているところに注目してください。

<select class="form-control" id="SupplierId" name="SupplierId">
  <option value="">NULL</option>
  <option value="1">Exotic Liquids</option>
  <option value="2">New Orleans Cajun Delights</option>
  <option value="3">Grandma Kelly's Homestead</option>
  ・・・中略・・・
</select>

これによりブラウザに表示されたドロップダウンリストを開くと NULL という選択肢が含まれるようになります。

NULL の選択

そして NULL を選択して POST すれば、データは SupplierId= という形 (name=value の value が空) でフォームに含まれて送信されます。

送信結果

モデルバインディングに使う Product クラスの定義は以下のようになっています。SupplierId, CategoryId プロパティの型が int? であるところに注目してください。

public partial class Product
{
    [Key]
    [Column("ProductID")]
    public int ProductId { get; set; }
    [Required]
    [StringLength(40)]
    public string ProductName { get; set; }
    [Column("SupplierID")]
    public int? SupplierId { get; set; }
    [Column("CategoryID")]
    public int? CategoryId { get; set; }

    // ・・・中略・・・
}

それをアクションメソッドの引数に使っていますので、POST されてきたデータは以下のようにモデルバインディングされます。SupplierId が null になっているところに注目してください。

モデルバインディング

これにより、自動生成されたアクションメソッドのコードで Products テーブルの当該レコードの SupplierID 列は NULL になります。

Tags: , ,

MVC

About this blog

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

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar