39
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MVC5とEntityFrameworkを使ってDBファーストでWebアプリケーションを作成するためのチュートリアル

Last updated at Posted at 2018-05-10

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という名前にしました。

1.png

2.png

次に、テーブルを追加します。今回は下記のようなデータが入ります。

◆カラム項目(カラム名):型

  • 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)
    );


実行後、下図のような状態になっていれば成功です。

3.png

次は作成したテーブルに使うデータを入れていきます。
下記の私の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;

テーブルのデータが正しく入っているか確認してください。

4.png

ここまででデータベースの作成は完了です。

Webアプリケーションの作成

では、本題のWebアプリケーションの作成に入っていきます。
まずはVisualStudioを起動しましょう。
上部メニューから ファイル→新規作成→プロジェクトを選択すると下記の画面が現れます。
右側のペインでVisualC#→Web→ASP.NET Webアプリケーションを選択し、名前を AddressPractice としてください。

5.png

OKを押して、次の画面ではMVCを選択します。

6.png

バージョンで多少の違いはあるかもしれませんが、下記のようなファイル/フォルダ構成になっていればOKです。

7.png

では次に、先ほど作成したDBを取り込んでいきます。

EntityFrameworkをインストールし、DBと連携する

まず初めにEntityFrameworkをプロジェクトにインストールします。
EntityFrameworkについての詳しい説明は譲りますが、ざっくり言うと「DBとアプリケーションの連携を簡潔に記述できるようにするもの(O/Rマッパー)の、ASP.NET対応版」です。

では、インストールの方法ですが、ソリューションエクスプローラ上で現在のプロジェクト名を右クリック→Nugetパッケージの管理 をクリックします。

8.png

現れたウィンドウで 参照 を選択し、EntityFrameworkを検索、インストールをクリックして完了です。

9.png

次に、最初に作成したDBと連携させます......が、その前にいったんビルドします。
上部メニューから ビルド→ソリューションのリビルドを実行してください。

無事完了したら、ソリューションエクスプローラー上のModelフォルダを右クリック→追加→新しい項目をクリックし下記のウィンドウを表示させます。

右のペインで データ を選択し、ADO.NET Entity Data Model を選択します。
名前を、EfPracticeContextに変更し追加をクリックしてください。

10.png

データベースからEFDesignerを選択し、次へ。

11.png

出てきた画面で、新しい接続をクリックします。

12.png

ここで、最初に作ったDBの情報が必要になるため、SSMSを立ち上げ、使うデータベース右クリック→プロパティを開きます。

13.png

出てきたウィンドウの 名前 の文字列をコピーしてください。

14.png

VisualStudioに戻って、コピーした文字列を サーバー名 の欄に貼り付けます。
そうすると、データベース名を選択できるようになるので、こちらもさきほどのEfPracticeを選択し、OKをクリック。

15.png

下記の画面に戻るので、次へをクリックします。

16.png

データベースオブジェクトを選択する画面が現れるので、テーブルにチェックを入れたら、完了をクリックします。

17.png

Modelの下に新たなファイルが作成されていればOKです。

18.png

ここでももう一度リビルドしておいてください
SSMSも閉じてOKです。

まずは単純なHelloWorldをやってみる

ここからはコードの記述に入っていきますが、初めにごくごく基本のHelloWorldをやってみます。

まず、ソリューションエクスプローラーのContorollerの上で右クリック→追加→コントローラ をクリックすると下記のウィンドウが現れるので、MVC5コントローラー-空を選択し、追加します。

19.png

名前は何でもよいのですが、一つ注意点があります。必ず末尾にControllerと付く名前にしてください。後ほど解説しますがURLのルーティングに関係しているためです。

20.png

スキャフォールディングが終わると、Controllerの下に HelloWorldController.cs ファイルが作られ、Views/HelloWorld というフォルダが新たに作成されます。

では、作成された HelloWorldController.cs の Index をドラッグしながら右クリック→ビューの追加をクリックします。

21.png

下記のウィンドウが現れるので、そのまま追加をクリックします。
22.png

Views/Helloworld の下に Index.cshtml というファイルが新たに作成されました。
23.png

では、ここに下記のコードをコピペしてください。


    @{
        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キーを押下)します。
以下の画面が現れれば成功です。
24.png

いったん画面を閉じましょう。

上記の例からわかるように、ASP.NET MVC5では、http://[サーバー名]/コントローラ名/ビュー名 という形でURLがルーティングされ、コントローラは “コントローラ名controller”にするという命名規則がある点に注意してください。(今回の例では HelloWorldがコントローラ名のため、HelloWorldController と名付けました)
また、コードの書き方からなんとなく読み取れると思いますが、アプリケーションの動作の流れを書くと以下のようになります (例:HelloWorld/Index の場合)

◆サーバーから見た動作

  1. クライアントから /HelloWorld/Index に対してGet要求が来る

  2. HelloWorldController.cs のActionResult Index() 内の処理を実行する
    →ここにReturn View() の記述がある場合、ActionResultと同名のビューHTMLをレスポンスとして渡す。 (Return Viewだけでなく、別のページへリダイレクトしたり、コントローラ内の別処理へ飛ばすことも当然可能です。)

  
基本的にはこの流れの2)の部分に様々な処理を追加していきます。


EntityFrameworkを使ったCRUDなコントローラとビューを作る

基本がつかめたところで、次にEntityFrameworkを使ってみます。
先ほどの流れと似ていますが、Controller上で右クリック→追加→新規スキャフォールディングアイテム を選択すると、以下のウィンドウが現れます。
ここで、一番上の EntityFrameworkを使用した、ビューがあるMVC5コントローラを選んで、追加を押下します。

25.png

現れたウィンドウで下記のように選択し、コントローラ名を addressControllerに変更し、追加を押下します。

26.png

さきほどのHelloWorldControllerと違い、色々と処理が書かれています。
が、基本は同じです。ActionResult ビュー名 になっており、Views/Addressの下には Create,Delete,Details,Edit,Indexというcshtmlファイルができています。
試しにこの状態で実行しましょう。
最初はHelloWorldが表示されますので、アドレスバーにURLを直接入力してください。

http://[サーバー名]/Helloworld/Index

となっているのを,

http://[サーバー名]/address/Index

に変更します。

以下のような画面が表示されると思います。

27.png

一行目の安藤さんの右端の列を見ると、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>

非常にシンプルな記述ですが、これでも一応表示はできます。リビルドして実行すると、下記の画面が表示されるはずです。

28.png

正直めっちゃ見づらいですね。簡便な記述になったとはいえ、出力がこれでは意味がありません。
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>

実行すると下図のようになります。

29.png

若干よくなりましたが、まだ気になる点がたくさんあります。とりあえず以下の二点を修正します。

  • 枠線がない
  • 列幅があってない

というわけで、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>

下記のようになります。よくなりましたが、まだ幅があっていません。

30.png

固定電話と住所のフリガナを非表示にし、各列に幅の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>

実行結果は以下になります。

31.png

最後に、ページャーを上下に表示するよう変更します。
本体のページャーを非表示にして、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>

実行結果


32.png



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>

ひとまずこの状態で表示してみます。

33.png

右端に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のリンクをクリックします。
そうすると、下記のような画面が表示されるはずです。

34.png

GetGeocodeボタンを押下すると、住所のポイントに地図が移動します。

また、下のほうにある逆ジオコードは、上記の住所とは全く関係なく、緯度経度から住所を表示する実装になっています。
こちらも色々触って、確認してみてください。

以上で今回のチュートリアルは終了です。
自分も初学者なため拙い部分多々あったかと思いますが、ご意見いただけますと幸いです。

宜しくお願いいたします。

39
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?