ASP.NET MVC5とEntityFramework6.xをベースとして、簡単なWebアプリケーションを作成するチュートリアルをまとめました。
以下のような方を対象にしています。
- プログラミング初学者
- 簡単なコンソールアプリケーションぐらいは研修で勉強した
- SQLをかじったことはある(Select, Insert, Delete, CreateTableぐらいは知ってる)
- Javaは多少経験があるが、ASP.NET(C#)は未経験
- EntityFrameworkの導入について知りたい
今回は、住所録をもとに地図を表示する機能 を目標に実装していきたいと思います。
環境
今回は以下の環境を使っています。
環境:
-
Windows10
-
VisualStudio2017 Community
-
SQL Server Express 2017
- SQL Server Management Studio 2017
まずはDBを用意する
今回はEntityFrameworkを使ったDBファーストという手法で開発を進めます。くだけた言い方をすると、先にデータベースを作成しておき、EntityFrameworkを使ってWebアプリとの橋渡しを行う開発手法といった感じです。
というわけで、まずは住所録を入れるDBの構築を行います。
SQL Server Management Studio (以下、SSMS)を起動し、データベースを作成しましょう。
オブジェクトエクスプローラーの[データベース]フォルダを右クリックし、データベースを追加します。今回はEfPracticeという名前にしました。
次に、テーブルを追加します。今回は下記のようなデータが入ります。
◆カラム項目(カラム名):型
- ID(ID):int 主キー
- 名前(name):VARCHAR(50)
- 名前フリガナ(furigana):VARCHAR(100)
- 性別(gender):CHAR(10) ※註1
- 血液型(bloodType):CHAR(10)
- 生年月日(birthDay):DATETIME
- 電話番号(homePhone):VARCHAR(15)
- 携帯番号(cellPhone):VARCHAR(15)
- メール(mailAddress):CHAR(256)
- 郵便番号(postalCode):CHAR(8)
- 住所(homeAddress):VARCHAR(200)
- 住所フリガナ(homeFurigana):VARCHAR(400)
註1:性別は本来下記リンクのように扱うべきですが、今回は練習ということでcharにしています。 https://qiita.com/yuba/items/567f8f47c9bb5a20200e
作成したDBに、下記のSQLを実行しましょう。(EfPracticeを右クリック→新しいクエリ)
use EfPractice;
create table ADDRESS_MASTER(
id int primary key,
name VARCHAR(50),
furigana VARCHAR(100),
gender CHAR(10),
bloodType CHAR(10),
birthDay DATE Null,
homePhone VARCHAR(15),
cellPhone VARCHAR(15),
mailAddress VARCHAR(256),
postalCode CHAR(8),
homeAddress VARCHAR(200),
homeFurigana VARCHAR(400)
);
実行後、下図のような状態になっていれば成功です。
次は作成したテーブルに使うデータを入れていきます。
下記の私のGitHubページにあるsanple_data.csvというファイルを右クリック→リンク先を保存 でファイルをダウンロードしてください。
https://github.com/ymstshinichiro/MVC5_tutorial
※参考:https://yamagata.int21h.jp/tool/testdata/
次に、先ほどと同様に新しいクエリを作成し、下記のSQLを実行します。
その際、読み込むファイルのパスはご自身がダウンロードしたファイルのパスを指定してください。
BULK INSERT ADDRESS_MASTER
FROM 'c:\address_data.csv'
WITH
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
);
select * from ADDRESS_MASTER;
テーブルのデータが正しく入っているか確認してください。
ここまででデータベースの作成は完了です。
Webアプリケーションの作成
では、本題のWebアプリケーションの作成に入っていきます。
まずはVisualStudioを起動しましょう。
上部メニューから ファイル→新規作成→プロジェクトを選択すると下記の画面が現れます。
右側のペインでVisualC#→Web→ASP.NET Webアプリケーションを選択し、名前を AddressPractice としてください。
OKを押して、次の画面ではMVCを選択します。
バージョンで多少の違いはあるかもしれませんが、下記のようなファイル/フォルダ構成になっていればOKです。
では次に、先ほど作成したDBを取り込んでいきます。
EntityFrameworkをインストールし、DBと連携する
まず初めにEntityFrameworkをプロジェクトにインストールします。
EntityFrameworkについての詳しい説明は譲りますが、ざっくり言うと「DBとアプリケーションの連携を簡潔に記述できるようにするもの(O/Rマッパー)の、ASP.NET対応版」です。
では、インストールの方法ですが、ソリューションエクスプローラ上で現在のプロジェクト名を右クリック→Nugetパッケージの管理 をクリックします。
現れたウィンドウで 参照 を選択し、EntityFrameworkを検索、インストールをクリックして完了です。
次に、最初に作成したDBと連携させます......が、その前にいったんビルドします。
上部メニューから ビルド→ソリューションのリビルドを実行してください。
無事完了したら、ソリューションエクスプローラー上のModelフォルダを右クリック→追加→新しい項目をクリックし下記のウィンドウを表示させます。
右のペインで データ を選択し、ADO.NET Entity Data Model を選択します。
名前を、EfPracticeContextに変更し追加をクリックしてください。
データベースからEFDesignerを選択し、次へ。
出てきた画面で、新しい接続をクリックします。
ここで、最初に作ったDBの情報が必要になるため、SSMSを立ち上げ、使うデータベース右クリック→プロパティを開きます。
出てきたウィンドウの 名前 の文字列をコピーしてください。
VisualStudioに戻って、コピーした文字列を サーバー名 の欄に貼り付けます。
そうすると、データベース名を選択できるようになるので、こちらもさきほどのEfPracticeを選択し、OKをクリック。
下記の画面に戻るので、次へをクリックします。
データベースオブジェクトを選択する画面が現れるので、テーブルにチェックを入れたら、完了をクリックします。
Modelの下に新たなファイルが作成されていればOKです。
ここでももう一度リビルドしておいてください。
SSMSも閉じてOKです。
まずは単純なHelloWorldをやってみる
ここからはコードの記述に入っていきますが、初めにごくごく基本のHelloWorldをやってみます。
まず、ソリューションエクスプローラーのContorollerの上で右クリック→追加→コントローラ をクリックすると下記のウィンドウが現れるので、MVC5コントローラー-空を選択し、追加します。
名前は何でもよいのですが、一つ注意点があります。必ず末尾にControllerと付く名前にしてください。後ほど解説しますがURLのルーティングに関係しているためです。
スキャフォールディングが終わると、Controllerの下に HelloWorldController.cs ファイルが作られ、Views/HelloWorld というフォルダが新たに作成されます。
では、作成された HelloWorldController.cs の Index をドラッグしながら右クリック→ビューの追加をクリックします。
下記のウィンドウが現れるので、そのまま追加をクリックします。
Views/Helloworld の下に Index.cshtml というファイルが新たに作成されました。
では、ここに下記のコードをコピペしてください。
@{
ViewBag.Title = "Hello World Page";
}
<div>
<h2>Hello World!</h2>
</div>
続いて、App_Start/ RoutteConfig.cs ファイルを開きます。19行目の
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
となっているところを、
defaults: new { controller = "HelloWorld", action = "Index", id = UrlParameter.Optional }
と、書き換えましょう。
この作業により、このアプリケーションを実行したときに最初にリクエストされるURLが、
http://[サーバー名]/HelloWorld/Index
となるよう変更されました。(※変更前は http://[サーバー名]/Home/Index だった)
完了したらファイルを上書き保存し、ビルド→実行(F5キーを押下)します。
以下の画面が現れれば成功です。
いったん画面を閉じましょう。
上記の例からわかるように、ASP.NET MVC5では、http://[サーバー名]/コントローラ名/ビュー名 という形でURLがルーティングされ、コントローラは “コントローラ名controller”にするという命名規則がある点に注意してください。(今回の例では HelloWorldがコントローラ名のため、HelloWorldController と名付けました)
また、コードの書き方からなんとなく読み取れると思いますが、アプリケーションの動作の流れを書くと以下のようになります (例:HelloWorld/Index の場合)
◆サーバーから見た動作
-
クライアントから /HelloWorld/Index に対してGet要求が来る
-
HelloWorldController.cs のActionResult Index() 内の処理を実行する
→ここにReturn View() の記述がある場合、ActionResultと同名のビューHTMLをレスポンスとして渡す。 (Return Viewだけでなく、別のページへリダイレクトしたり、コントローラ内の別処理へ飛ばすことも当然可能です。)
基本的にはこの流れの2)の部分に様々な処理を追加していきます。
EntityFrameworkを使ったCRUDなコントローラとビューを作る
基本がつかめたところで、次にEntityFrameworkを使ってみます。
先ほどの流れと似ていますが、Controller上で右クリック→追加→新規スキャフォールディングアイテム を選択すると、以下のウィンドウが現れます。
ここで、一番上の EntityFrameworkを使用した、ビューがあるMVC5コントローラを選んで、追加を押下します。
現れたウィンドウで下記のように選択し、コントローラ名を addressControllerに変更し、追加を押下します。
さきほどのHelloWorldControllerと違い、色々と処理が書かれています。
が、基本は同じです。ActionResult ビュー名 になっており、Views/Addressの下には Create,Delete,Details,Edit,Indexというcshtmlファイルができています。
試しにこの状態で実行しましょう。
最初はHelloWorldが表示されますので、アドレスバーにURLを直接入力してください。
http://[サーバー名]/Helloworld/Index
となっているのを,
http://[サーバー名]/address/Index
に変更します。
以下のような画面が表示されると思います。
一行目の安藤さんの右端の列を見ると、Edit,,Detail,Deleteとありますが、これらの各リンクをクリックすると、それぞれ編集、詳細、削除の画面に遷移します。
また、左上のCreate New というリンクをクリックすると、新しいデータを入力できる画面が現れます。
このように、EntityFrameworkを使うとデータの操作を行うWebアプリケーションのひな型を簡単に作成することができます。
それぞれのページでどのような処理が動き、どのようなページがViewとして表現されているのか、自分で動かしながら調べてみてください。
WebGridを使ってみる
Views/address/Indexの中身を見てみると、モデルのデータをforeachで順番に表示しているのが見てとれます。無駄な処理をしているわけではないのですが、よりスマートな解決方法としてWebGridというオブジェクトを使う方法があるので紹介します。
まず初めに、addressController.csに以下のコードをコピペしてください。
public ActionResult GridIndex()
{
IEnumerable<ADDRESS_MASTER> address = db.ADDRESS_MASTER.Select(x => x);
return View(address);
}
次に、Views/Addressの下に GridIndex.cshtml というファイルを追加し、下記をコピペしてください。
@model IEnumerable<AddressPractice.Models.ADDRESS_MASTER>
@{
ViewBag.Title = "GridIndex";
}
<h2>GridIndex</h2>
@{
var grid = new WebGrid();
grid.Bind(Model);
}
<div class="row">
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid", headerStyle: "head", footerStyle: "table-pager");
}
</div>
</div>
非常にシンプルな記述ですが、これでも一応表示はできます。リビルドして実行すると、下記の画面が表示されるはずです。
正直めっちゃ見づらいですね。簡便な記述になったとはいえ、出力がこれでは意味がありません。
GridIndex.cshtmlを編集して、きれいにしていきます。
まずは下記のようにタイトルに表示する文字列を変更します。
@model IEnumerable<AddressPractice.Models.ADDRESS_MASTER>
@{
ViewBag.Title = "GridIndex";
var grid = new WebGrid(source: Model);
}
<h2>GridIndex</h2>
<div class="row">
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid",
headerStyle: "head",
footerStyle: "table-pager",
columns:grid.Columns
(
grid.Column("name", "名前"),
grid.Column("furigana", "フリガナ"),
grid.Column("gender", "性別"),
grid.Column("birthDay", "生年月日"),
grid.Column("bloodType", "血液型"),
grid.Column("homePhone", "固定電話"),
grid.Column("cellPhone", "携帯電話"),
grid.Column("mailAddress", "メールアドレス"),
grid.Column("postalCode", "郵便番号"),
grid.Column("homeAddress", "住所"),
grid.Column("homeFurigana", "フリガナ")
)
)
}
</div>
</div>
実行すると下図のようになります。
若干よくなりましたが、まだ気になる点がたくさんあります。とりあえず以下の二点を修正します。
- 枠線がない
- 列幅があってない
というわけで、GridIndex.cshtml にインラインでCSSを入れてみます。
@model IEnumerable<AddressPractice.Models.ADDRESS_MASTER>
@{
ViewBag.Title = "GridIndex";
var grid = new WebGrid(source: Model);
}
<style type="text/css">
.grid { table-layout:fixed; border-collapse: collapse;}
.head { background-color: #E8E8E8; font-weight: bold; color: #FFF}
.grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px;column-width: 200px }
.grid th { text-align: center;}
.alt { background-color: #E8E8E8; color: #000; }
.product { width: 200px; font-weight:bold;}
</style>
<h2>GridIndex</h2>
<div class="row">
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid",
headerStyle: "head",
footerStyle: "table-pager",
columns:grid.Columns
(
grid.Column("name", "名前"),
grid.Column("furigana", "フリガナ"),
grid.Column("gender", "性別"),
grid.Column("birthDay", "生年月日"),
grid.Column("bloodType", "血液型"),
grid.Column("homePhone", "固定電話"),
grid.Column("cellPhone", "携帯電話"),
grid.Column("mailAddress", "メールアドレス"),
grid.Column("postalCode", "郵便番号"),
grid.Column("homeAddress", "住所"),
grid.Column("homeFurigana", "フリガナ")
)
)
}
</div>
</div>
下記のようになります。よくなりましたが、まだ幅があっていません。
固定電話と住所のフリガナを非表示にし、各列に幅のStyleを追加しました。
また、生年月日のフォーマットを変更し、時刻を表示しないようにしています。
@model IEnumerable<AddressPractice.Models.ADDRESS_MASTER>
@{
ViewBag.Title = "GridIndex";
var grid = new WebGrid(source: Model);
}
<style type="text/css">
.grid { table-layout:fixed; border-collapse: collapse;}
.head { background-color: #E8E8E8; font-weight: bold; color: #FFF}
.grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px;}
.grid th { text-align: center;}
.alt { background-color: #E8E8E8; color: #000; }
.product { width: 200px; font-weight:bold;}
.ColumnWid1 {column-width:10px}
.ColumnWid2 {column-width: 30px}
.ColumnWid3 {column-width: 80px}
.ColumnWid4 {column-width: 150px}
.ColumnWid5 {column-width: 180px}
</style>
<h2>GridIndex</h2>
<div class="row">
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid",
headerStyle: "head",
footerStyle: "table-pager",
columns: grid.Columns
(
grid.Column("name", "名前", style: "ColumnWid3"),
grid.Column("furigana", "フリガナ", style: "ColumnWid3"),
grid.Column("gender", "性別", style: "ColumnWid1"),
grid.Column("birthDay", "生年月日", style: "ColumnWid3", format: (item) => string.Format("{0:yyyy/MM/dd}", item.birthDay)),
grid.Column("bloodType", "血液型",style: "ColumnWid2"),
//grid.Column("homePhone", "固定電話",style: "ColumnWid1"),
grid.Column("cellPhone", "携帯電話",style: "ColumnWid3"),
grid.Column("mailAddress", "メールアドレス",style: "ColumnWid4"),
grid.Column("postalCode", "郵便番号",style: "ColumnWid3"),
grid.Column("homeAddress", "住所",style: "ColumnWid5")
//grid.Column("homeFurigana", "フリガナ",style: "ColumnWid1")
)
)
}
</div>
</div>
実行結果は以下になります。
最後に、ページャーを上下に表示するよう変更します。
本体のページャーを非表示にして、grid.pager()を本体の上下で呼び出せばOKです。
@model IEnumerable<AddressPractice.Models.ADDRESS_MASTER>
@{
ViewBag.Title = "GridIndex";
var grid = new WebGrid(source: Model);
}
<style type="text/css">
.grid { table-layout:fixed; border-collapse: collapse;}
.head { background-color: #E8E8E8; font-weight: bold; color: #FFF}
.grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px;}
.grid th { text-align: center;}
.alt { background-color: #E8E8E8; color: #000; }
.product { width: 200px; font-weight:bold;}
.ColumnWid1 {column-width:10px}
.ColumnWid2 {column-width: 30px}
.ColumnWid3 {column-width: 80px}
.ColumnWid4 {column-width: 150px}
.ColumnWid5 {column-width: 180px}
.table-pager { display: none; @* ←これで元々あるPagerを非表示にしてる*@; }
.pager {
vertical-align: middle;
border-color: inherit;
padding-top: 10px;
}
</style>
<h2>GridIndex</h2>
<div class="row">
<div class="pager">
@*DivでくくるとPagerに別のCssを当てれる*@
@grid.Pager()
</div>
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid",
headerStyle: "head",
footerStyle: "table-pager",
columns: grid.Columns
(
grid.Column("name", "名前", style: "ColumnWid3"),
grid.Column("furigana", "フリガナ", style: "ColumnWid3"),
grid.Column("gender", "性別", style: "ColumnWid1"),
grid.Column("birthDay", "生年月日", style: "ColumnWid3", format: (item) => string.Format("{0:yyyy/MM/dd}", item.birthDay)),
grid.Column("bloodType", "血液型", style: "ColumnWid2"),
//grid.Column("homePhone", "固定電話",style: "ColumnWid1"),
grid.Column("cellPhone", "携帯電話", style: "ColumnWid3"),
grid.Column("mailAddress", "メールアドレス", style: "ColumnWid4"),
grid.Column("postalCode", "郵便番号", style: "ColumnWid3"),
grid.Column("homeAddress", "住所", style: "ColumnWid5")
//grid.Column("homeFurigana", "フリガナ",style: "ColumnWid1")
)
)
}
</div>
<div class="pager">
@grid.Pager()
</div>
</div>
実行結果
google Maps APIを使って地図を表示してみる
仕上げに、住所を地図上でも表示できるようにしてみたいと思います。
ご存知googleMapを活用していくわけですが初めの準備が少々面倒です。
まず、googleアカウントが無い方はアカウントをとってください。
アカウントができたら下記のリンクに従ってAPIキーを取得しましょう。
https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/
キーの制限 という項目まで進んだら、リファラーを */address/* に設定しておいてください。
ではまず、さきほど作ったGridIndex.cshtmlに一部追記をします。
<div class="col-lg-12">
@*formでくくるのを忘れずに*@
@using (Html.BeginForm())
{
@grid.GetHtml(tableStyle: "grid",
headerStyle: "head",
footerStyle: "table-pager",
columns: grid.Columns
(
grid.Column("name", "名前", style: "ColumnWid3"),
grid.Column("furigana", "フリガナ", style: "ColumnWid3"),
grid.Column("gender", "性別", style: "ColumnWid1"),
grid.Column("birthDay", "生年月日", style: "ColumnWid3", format: (item) => string.Format("{0:yyyy/MM/dd}", item.birthDay)),
grid.Column("bloodType", "血液型", style: "ColumnWid2"),
//grid.Column("homePhone", "固定電話",style: "ColumnWid1"),
grid.Column("cellPhone", "携帯電話", style: "ColumnWid3"),
grid.Column("mailAddress", "メールアドレス", style: "ColumnWid4"),
grid.Column("postalCode", "郵便番号", style: "ColumnWid3"),
grid.Column("homeAddress", "住所", style: "ColumnWid5"), @*←カンマ追加してます*@
//grid.Column("homeFurigana", "フリガナ",style: "ColumnWid1")
grid.Column("homeAddress", "住所", format: (item) => @Html.ActionLink( "map" , "Details",new { id = item.id } )) @*←あと、この行を追加*@
)
)
}
</div>
ひとまずこの状態で表示してみます。
右端にmap というリンクが追加されました。
押してみると、住所録の詳細に遷移します。
この詳細の下に地図を表示したいと思います。
では、Views/address/Details.cshtml を開き、すでにあるコードの最下行から、以下をコピペで付け足してください。
`@* ↓自分のAPIキーを入れる *@
<script src="//maps.googleapis.com/maps/api/js?key=ここにAPIキー&libraries=places,geometry&sensor=false"></script>
<body onload="initialize()">
<table style="width:500px;border:0;">
<tr><td>緯度</td><td id="id_ido"></td></tr>
<tr><td>経度</td><td id="id_keido"></td></tr>
</table>
<input id="address" type="text" value= @Model.homeAddress.ToString() >
<input type="button" value="GetGeocode" onclick="codeAddress()">
<div id="map_canvas" style="width:700px; height:500px"></div>
<br />
<br />
@Html.Label("逆ジオコード")
<table style="width:500px;border:0;">
<tr><td>緯度</td><td><input id="id_ido_rev" type="text" value=35.681167></td></tr>
<tr><td>経度</td><td><input id="id_keido_rev" type="text" value=139.767052></td></tr>
<tr><td>住所</td><td id="id_Address_rev"></td></tr>
</table>
<input type="button" value="Reverse Geocode" onclick="codeAddress_rev()">
</body>
<p>
@Html.ActionLink("Edit", "Edit", new { id = Model.id }) |
@Html.ActionLink("Back to List", "Index")
</p>
一番上の ここにAPIキー と書かれた部分を、自分のAPIキーに書き換えたら、まずはさきほどのGridIndexを表示させて、Mapのリンクをクリックします。
そうすると、下記のような画面が表示されるはずです。
GetGeocodeボタンを押下すると、住所のポイントに地図が移動します。
また、下のほうにある逆ジオコードは、上記の住所とは全く関係なく、緯度経度から住所を表示する実装になっています。
こちらも色々触って、確認してみてください。
以上で今回のチュートリアルは終了です。
自分も初学者なため拙い部分多々あったかと思いますが、ご意見いただけますと幸いです。
宜しくお願いいたします。