概要
ASP.NET MVC + MySQLを使用してDBファーストでWebアプリケーションを作成します。
今回はDBファーストでのMySQLとの連携とWebページの表示までを行います。
環境
- MySQL 8.0.20
- Visual Studio 2019
- .Net Framework 4.8
- MySql.Data 8.0.20
- MySql.Data.EntityFramework 8.0.20
DB
今回使用するDBは以下のようなテーブル構成です。
各データは下記リンク先から取得しました。
気象データ:http://www.data.jma.go.jp/obd/stats/etrn/index.php
都道府県:http://www007.upp.so-net.ne.jp/s124/jis.html
市区町村:https://www.soumu.go.jp/denshijiti/code.html
※市区町村は都道府県名をprefectureのidになるように加工しています。
テーブル構成
テーブル | 内容 |
---|---|
city | 市区町村マスタ |
prefecture | 都道府県マスタ |
weather | 気象データ |
SQL
CREATEDATABASE`weather`/*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci *//*!80016 DEFAULT ENCRYPTION='N' */;
CREATETABLE`city`(`id`intNOTNULL,`prefecture_id`intNOTNULL,`name`varchar(45)NOTNULL,PRIMARYKEY(`id`),KEY`prefecture_fkey_idx_idx`(`prefecture_id`),CONSTRAINT`prefecture_fkey_idx`FOREIGNKEY(`prefecture_id`)REFERENCES`prefecture`(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci;
CREATETABLE`prefecture`(`id`intNOTNULL,`name`varchar(45)NOTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci;
CREATETABLE`weather`(`observational_day`datetimeNOTNULL,`city_id`intNOTNULL,`temperature_ave`decimal(5,2)NOTNULL,`temperature_min`decimal(5,2)NOTNULL,`temperature_max`decimal(5,2)NOTNULL,`precipitation`decimal(5,2)NOTNULL,`wind_speed`decimal(5,2)NOTNULL,PRIMARYKEY(`observational_day`,`city_id`),KEY`city_fkey_idx_idx`(`city_id`),CONSTRAINT`city_fkey_idx`FOREIGNKEY(`city_id`)REFERENCES`city`(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci;
Web アプリの作成
Visual Studio から [新しいプロジェクトの作成] を選択します。
[ASP.NET Web アプリケーション] を選択し、 [次へ] をクリックします。
プロジェクト名等を設定し、[作成] をクリックします。
MVCを選択し、[作成]をクリックします。
MySQLとの連携
コンポーネントのインストール
[NuGet パッケージの管理]から以下をインストールします。
- MySql.Data
- MySql.Data.EntityFramework
※MySql.Data.Entityは不要です。
モデルの作成
[新しい項目の追加]から[ADO.NET Entry Data Model]を選択します。
今回は名前に「MySqlModel」を設定します。
[データベースからEF Designer]を選択し、[次へ]をクリックします。
[Entry Data Modelウィザード]の[新しい接続]をクリックします。
[接続のプロパティ]のデータソースを[MySQL Database (MySQL Data Provider)]に変更し、DB接続情報を設定後、[OK]をクリックします。
パスワードを接続文字列に含めるかどうかを選択し、[次へ] をクリックします。
モデルに含めるオブジェクトを選択し、[完了]をクリックします。
今回はテーブルを全て選択します。
完了後、[Web.config]に接続文字列が追加されます。
コントローラー作成
ソリューションエクスプローラーのControllersディレクトリ上で右クリック→[追加]→[コントローラー]を選択します。
[Entity Frameworkを使用した、ビューがあるMVC5コントローラー]を選択し、[追加]をクリックします。
[モデルクラス]で使用するモデル(weather)を選択する。
[データコンテキストクラス]は先ほど作成したweatherEntitiesを選択する。
[コントローラー名]のデフォルト値をweathersControllerからWeatherControllerに変更する。
※変えなくてもOK
入力が完了したら、[追加]ボタンをクリックします。
「プロジェクトをビルドしなおしてください。」というエラーが出た場合は、ビルド後に再実行してください。
自動生成されたコントローラーのIndex()
publicActionResultIndex(){varweather=db.weather.Include(w=>w.city);returnView(weather.ToList());}
自動作成されたIndexビュー
@modelIEnumerable<MySqlTestWebApp.weather>@{ViewBag.Title="Index";}<h2>Index</h2><p>@Html.ActionLink("Create New","Create")</p><tableclass="table">
<tr><th>@Html.DisplayNameFor(model=>model.temperature_ave)</th><th>@Html.DisplayNameFor(model=>model.temperature_min)</th><th>@Html.DisplayNameFor(model=>model.temperature_max)</th><th>@Html.DisplayNameFor(model=>model.precipitation)</th><th>@Html.DisplayNameFor(model=>model.wind_speed)</th><th>@Html.DisplayNameFor(model=>model.city.name)</th><th></th></tr>@foreach(variteminModel){<tr><td>@Html.DisplayFor(modelItem=>item.temperature_ave)</td><td>@Html.DisplayFor(modelItem=>item.temperature_min)</td><td>@Html.DisplayFor(modelItem=>item.temperature_max)</td><td>@Html.DisplayFor(modelItem=>item.precipitation)</td><td>@Html.DisplayFor(modelItem=>item.wind_speed)</td><td>@Html.DisplayFor(modelItem=>item.city.name)</td><td>@Html.ActionLink("Edit","Edit",new{/* id=item.PrimaryKey */})|@Html.ActionLink("Details","Details",new{/* id=item.PrimaryKey */})|@Html.ActionLink("Delete","Delete",new{/* id=item.PrimaryKey */})</td></tr>}</table>
ページ
Index.cshtmlを表示した状態でCtrl + F5(またはデバッグを実行)でページを表示します。
weatherテーブル
DBのweatherテーブルのデータを取得できていることが確認できます。
weatherテーブルの[city_id]はcityテーブルの[id]を外部キーに設定しているため、自動で名前が表示されます。
ビュー(Index.cshtml)の修正
- 主キーである[observational_day]が表示されていないため追加します。
- 市区町村が2列目になるようにします。
- Edit、Details、Deleteのリンク部分のキーを設定します。
※主キーが[id]であれば自動で設定されますが、今回は違うので修正します。
修正後
@modelIEnumerable<MySqlTestWebApp.weather>@{ViewBag.Title="Index";}<h2>Index</h2><p>@Html.ActionLink("Create New","Create")</p><tableclass="table">
<tr><th>@Html.DisplayNameFor(model=>model.observational_day)</th><th>@Html.DisplayNameFor(model=>model.city.name)</th><th>@Html.DisplayNameFor(model=>model.temperature_ave)</th><th>@Html.DisplayNameFor(model=>model.temperature_min)</th><th>@Html.DisplayNameFor(model=>model.temperature_max)</th><th>@Html.DisplayNameFor(model=>model.precipitation)</th><th>@Html.DisplayNameFor(model=>model.wind_speed)</th><th></th></tr>@foreach(variteminModel){<tr><td>@Html.DisplayFor(modelItem=>item.observational_day)</td><td>@Html.DisplayFor(modelItem=>item.city.name)</td><td>@Html.DisplayFor(modelItem=>item.temperature_ave)</td><td>@Html.DisplayFor(modelItem=>item.temperature_min)</td><td>@Html.DisplayFor(modelItem=>item.temperature_max)</td><td>@Html.DisplayFor(modelItem=>item.precipitation)</td><td>@Html.DisplayFor(modelItem=>item.wind_speed)</td><td>@Html.ActionLink("Edit","Edit",new{observationalDay=item.observational_day,cityID=item.city_id})|@Html.ActionLink("Details","Details",new{observationalDay=item.observational_day,cityID=item.city_id})|@Html.ActionLink("Delete","Delete",new{observationalDay=item.observational_day,cityID=item.city_id})|</td></tr>}</table>
コントローラー(WeatherController)の修正
- 主キーである[observational_day]、[city_id]を使用するように各メソッドを修正します。
※引数の変数名とビューのActionLinkに設定した変数名を揃える必要があります。
修正前
publicActionResultDetails(DateTimeid){if(id==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(id);if(weather==null){returnHttpNotFound();}returnView(weather);}publicActionResultEdit(DateTimeid){if(id==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(id);if(weather==null){returnHttpNotFound();}ViewBag.city_id=newSelectList(db.city,"id","name",weather.city_id);returnView(weather);}publicActionResultDelete(DateTimeid){if(id==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(id);if(weather==null){returnHttpNotFound();}returnView(weather);}[HttpPost,ActionName("Delete")][ValidateAntiForgeryToken]publicActionResultDeleteConfirmed(DateTimeid){weatherweather=db.weather.Find(id);db.weather.Remove(weather);db.SaveChanges();returnRedirectToAction("Index");}
修正後
publicActionResultDetails(DateTimeobservationalDay,int?cityID){if(observationalDay==null||cityID==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(observationalDay,cityID);if(weather==null){returnHttpNotFound();}returnView(weather);}publicActionResultEdit(DateTimeobservationalDay,int?cityID){if(observationalDay==null||cityID==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(observationalDay,cityID);if(weather==null){returnHttpNotFound();}ViewBag.city_id=newSelectList(db.city,"id","name",weather.city_id);returnView(weather);}publicActionResultDelete(DateTimeobservationalDay,int?cityID){if(observationalDay==null||cityID==null){returnnewHttpStatusCodeResult(HttpStatusCode.BadRequest);}weatherweather=db.weather.Find(observationalDay,cityID);if(weather==null){returnHttpNotFound();}returnView(weather);}[HttpPost,ActionName("Delete")][ValidateAntiForgeryToken]publicActionResultDeleteConfirmed(DateTimeobservationalDay,int?cityID){weatherweather=db.weather.Find(observationalDay,cityID);db.weather.Remove(weather);db.SaveChanges();returnRedirectToAction("Index");}
モデルの修正
日本語の列名を表示させるため、[DisplayName]を設定します。
それから、[observational_day]には[DisplayFormat]を使用して日付のフォーマットを設定します。
修正前
namespaceMySqlTestWebApp{usingSystem;usingSystem.Collections.Generic;publicpartialclassweather{publicSystem.DateTimeobservational_day{get;set;}publicintcity_id{get;set;}publicdecimaltemperature_ave{get;set;}publicdecimaltemperature_min{get;set;}publicdecimaltemperature_max{get;set;}publicdecimalprecipitation{get;set;}publicdecimalwind_speed{get;set;}publicvirtualcitycity{get;set;}}}
namespaceMySqlTestWebApp{usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;publicpartialclasscity{[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2214:DoNotCallOverridableMethodsInConstructors")]publiccity(){this.weather=newHashSet<weather>();}publicintid{get;set;}publicintprefecture_id{get;set;}publicstringname{get;set;}[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]publicvirtualICollection<weather>weather{get;set;}publicvirtualprefectureprefecture{get;set;}}}
修正後
namespaceMySqlTestWebApp{usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.ComponentModel.DataAnnotations;publicpartialclassweather{[DisplayName("観測日")][DisplayFormat(ApplyFormatInEditMode=true,DataFormatString="{0:yyyy/MM/dd}")]publicSystem.DateTimeobservational_day{get;set;}publicintcity_id{get;set;}[DisplayName("平均気温 ℃")]publicdecimaltemperature_ave{get;set;}[DisplayName("最低気温 ℃")]publicdecimaltemperature_min{get;set;}[DisplayName("最高気温 ℃")]publicdecimaltemperature_max{get;set;}[DisplayName("降水量 mm")]publicdecimalprecipitation{get;set;}[DisplayName("最大瞬間風速 m/s")]publicdecimalwind_speed{get;set;}publicvirtualcitycity{get;set;}}}
namespaceMySqlTestWebApp{usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;publicpartialclasscity{[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2214:DoNotCallOverridableMethodsInConstructors")]publiccity(){this.weather=newHashSet<weather>();}publicintid{get;set;}publicintprefecture_id{get;set;}[DisplayName("市区町村")]publicstringname{get;set;}[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]publicvirtualICollection<weather>weather{get;set;}publicvirtualprefectureprefecture{get;set;}}}
修正後のページ
見た目も多少それらしくなったのではないかと思います。
まとめ
今回はDBファーストでのMySQLとの連携とWebページの表示までを行いました。
普段はWeb系の開発はやらないので難しい印象がありましたが、DBの情報を使用してコントローラやビューを自動で作成してくれるので、かなり助かりました。
参考
https://docs.microsoft.com/ja-jp/aspnet/mvc/overview/getting-started/database-first-development/
https://qiita.com/mrpero/items/607c31895d77815a77cb