ASP.NET MVC5 をざっとペロる

# :dizzy: 環境

* Windows 8.1 pro
* SQL Server 2014 Express

# :dizzy: このざっペロの目標

ASP.NET MVC5 特有のことを何かするわけでもないけど、ざーっと Model-View-Controller についてペロる

# :dizzy: プロジェクトの作成

![スクリーンショット 2015-05-18 23.47.38.png](https://qiita-image-store.s3.amazonaws.com/0/9988/7643dee4-9cee-bffd-1e87-45a6ce04ddc9.png)

Visual C# から Web を選択

![スクリーンショット 2015-05-18 23.48.09.png](https://qiita-image-store.s3.amazonaws.com/0/9988/dc3edc2e-1755-3736-a907-484f03c57ae7.png)

Empty を選択し、下部「以下フォルダー及びコア参照を追加：」で「MVC」にチェック。Microsoft Azure の 「クラウド内のホスト」のチェックが外れていることを確認

![スクリーンショット 2015-05-18 23.48.27.png](https://qiita-image-store.s3.amazonaws.com/0/9988/a7421ac9-ab48-1e55-c3e2-f23de2dcf438.png)

＊実際に作ったプロジェクト名は Chrowa3 だけど適当にどうぞ

# :dizzy: Controller

Controller を MyProject/Controllers/HelloWorldController.cs として用意

![スクリーンショット 2015-05-16 23.35.12.png](https://qiita-image-store.s3.amazonaws.com/0/9988/2803f87e-8099-3ff0-7126-2a94ff050f06.png)

HelloWorldController.cs の中身

```csharp:HelloWorldController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Chrowa3.Controllers
{
    public class HelloWorldController : Controller
    {
        // アクションメソッド
        public ActionResult Index()
        {
            return Content("Hello World");
        }

    }
}
```

## :octocat: ポイント
 
よくある MVC FW と同様で

* コントローラークラス名は必ず「Controller」を接尾辞とする
* 作成するコントローラーはController クラスを継承 ( : Controller )
* アクションメソッドでリクエストに応じた処理を実行 ( public ActionResult Index() )

```
// 「Controller」を接尾辞とする例
FooController.cs
BarController.cs
```




### アクションメソッドの戻り値は ActionResult オブジェクト

* Contentメソッドは、ContentReusut オブジェクトを生成する。
* ContentResultクラスは ActionResultクラスの派生クラス。
* Contentメソッドは、Controllerクラスで用意された「ヘルパーメソッド」である

## :octocat: 確認してみる

アクションメソッド Index() は、/HelloWorld/Index の様にアドレスを指定することで呼び出すことができる。

```
http://chrowa3/HelloWorld/Index
```

URLに関する設定（ルーティング）がデフォルトの設定のままの場合は Indexメソッドはデフォルト値として設定されているので省略できる

```
http://chrowa3/HelloWorld/
```

![スクリーンショット 2015-05-17 0.04.16.png](https://qiita-image-store.s3.amazonaws.com/0/9988/d510afb9-c738-35d9-c307-fa8d80f1fd8a.png)



## :octocat: アクションメソッドを追加してみる

```csharp:HelloWorldController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Chrowa3.Controllers
{
    public class HelloWorldController : Controller
    {
        // GET: HelloWorld
        public ActionResult Index()
        {
            return Content("Hello World");
        }

        // 追加のアクションメソッド
        public ActionResult Foo()
        {
            return Content("bar");
        }
    }
}
```


### 確認

```
http://chrowa3/HelloWorld/Foo
```

![スクリーンショット 2015-05-17 0.08.11.png](https://qiita-image-store.s3.amazonaws.com/0/9988/45461791-7766-6bdc-17f1-0f84543444bc.png)


# :dizzy: ルーティング

ルーティングとは、リクエストされた URL に応じて呼び出す処理を決定する仕組みです。もう少し具体的に言うと、クライアントから要求されたURLに応じて呼び出すコントローラーやアクションを決定する。

ルーティングは MyProject/App_Start/RouteConfig.cs で定義されているよ。

![スクリーンショット 2015-05-17 7.36.31.png](https://qiita-image-store.s3.amazonaws.com/0/9988/fe2032e6-7848-018e-0036-b6c81bff5d87.png)

RouteConfig.cs を含む App_Start フォルダにある cs ファイルは、アプリケーションの起動時にGlobal.asaxによって呼び出される。これらは大抵の場合 static メソッドで構成され、フォルダの名前の通り初期化を担う役割を持っています。

```csharp:RouteConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Chrowa3
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}
```

ルーティングの設定は MapRoute( )メソッドで追加していきます。デフォルトでは名前付き引数を使っているので、なんの値を設定しているのかすごくわかりやすいよね。（引数はデフォルト値が設定されていないので省略不可能）

```csharp
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
```

### 各引数について

| 引数 | 説明 |
|---|------------|
| name | ルーティング設定を識別するための名前。一意であれば自由に設定できる |
| url | URLパターン。{ } はプレースホルダーで、controllerやactionは予約されている名前で、それぞれコントローラー名、アクションメソッド名と紐づく。 {id} は任意の文字列で設定してもいいし、受け取るパラメーターはいくらでも増やせる|
| default | リクエストに対するデフォルト値を設定。たとえば name: default の設定では、controller を HomeController に、action を Index( ) に、id を UrlParameter.Optional に設定している |

例）引数を2つ受け取るルーティング
　name: "Sample" という設定を追加してみた。URL パターンに {foo}/{bar} を追加

```csharp:RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
	routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

	routes.MapRoute(
		name: "Sample",
		url: "{controller}/{action}/{foo}/{bar}",
		defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
	);

	routes.MapRoute(
		name: "Default",
		url: "{controller}/{action}/{id}",
		defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
	);
}
```

先ほど追加したアクションメソッド Foo を以下のように変更

```csharp:HelloWorldController.cs
public class HelloWorldController : Controller
{
	// GET: HelloWorld
	public ActionResult Index()
	{
		return Content("Hello World");
	}

	public ActionResult Foo(string foo,string bar)
	{
		return Content(foo + " " + bar); ;
	}
}
```

結果

![スクリーンショット 2015-05-17 12.04.28.png](https://qiita-image-store.s3.amazonaws.com/0/9988/777f8efd-e036-83e9-753c-7a036c6784d7.png)

＊Foo( ) の引数がルーティング側の引数名と同一でないと受け取れないので注意

## :octocat: Global.asax

RouteConfig.RegisterRoutes(RouteTable.Routes) で ルーティングを呼び出している

```csharp:Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Chrowa3
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}
```
　
# :dizzy: View

## :octocat: Razor

Razor は ASPX に変わる比較的新し目のビューエンジンで、コードを @ から表し、<% ... %> のように終了デリミタの要らない書きやすい仕様となっている。

ってことで、まずは Bar という名前の View から作ってみるよ。場所は MyProject/Views/HelloWorld に作成するよ。

![スクリーンショット 2015-05-17 12.31.57.png](https://qiita-image-store.s3.amazonaws.com/0/9988/c50226c0-233e-9917-ee8e-772ff30d8337.png)

追加したいフォルダ上で右クリックすれば簡単に View の追加ができるお

![スクリーンショット 2015-05-17 12.36.43.png](https://qiita-image-store.s3.amazonaws.com/0/9988/2e871731-535f-0913-b433-dd759f25494e.png)

こんな感じで

![スクリーンショット 2015-05-17 12.36.59.png](https://qiita-image-store.s3.amazonaws.com/0/9988/6941930c-8080-d964-d660-692c55680668.png)

中身

```html:Bar.csthml
<!DOCTYPE html>

<html>
<head>
    <meta charset="UTF-8">
    <title>Bar</title>
</head>
<body>
    <div>
        <h1>View Sample</h1>
        <p>@ViewBag.Message</p> 
    </div>
</body>
</html>
```

VeiwBag は下部ソースの通り dynamic 型で定義されているので、型をあんまり気にせずにバインドできちゃいます。

```csharp
//
// 概要:
//     動的なビュー データ ディクショナリを取得します。
//
// 戻り値:
//     動的なビュー データのディクショナリ。
[Dynamic]
public dynamic ViewBag { get; }
```

csharp:HelloWorldController に Bar というアクションメソッドを追加

```csharp:HelloWorldController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Chrowa3.Controllers
{
    public class HelloWorldController : Controller
    {
        // GET: HelloWorld
        public ActionResult Index()
        {
            return Content("Hello World");
        }

        public ActionResult Bar()
        {
            ViewBag.Message = "Hello View ! Hello Bar !";
            return View();
        }

        public ActionResult Foo(string foo,string bar)
        {
            return Content(foo + " " + bar); ;
        }
    }
}
```

ここで重要なのが、View名とアクションメソッド名が同名になっているということ（名前で紐付いている）。もっと言えば View ファイルである cshtml は

```
/コントローラー名/アクション名.cshtml
```
である必要があるよ。

## :octocat: レイアウトを適用する

レイアウトは、複数のページで共通する様なHTMLを共有化する手段を提供してくれるよ。Viewsディレクトリのなかに Shared ディレクトリを作成し、_BaseLayout.cshtml を用意。

Razor では _（アンダースコアで始まるテンプレートを「レイアウト」や「部分ビュー」として認識するので、必ず _（アンダースコア）で始まるファイル名で定義する。

![スクリーンショット 2015-05-17 22.18.49.png](https://qiita-image-store.s3.amazonaws.com/0/9988/6a42b155-ab07-74c3-2204-11be0216a10e.png)

今回はこんな感じで用意してみた

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>@ViewBag.Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    @RenderBody()
</body>
</html>
```

ViewBag.Title は 先ほどの ViewBag.Message を同じ考え方で理解できるよ。
RenderBody() では 対象の View ファイルがバインドされるイメージで

Bar.cshtml を以下の様に変更

```html:Bar.csthml

@{
    Layout = "~/Views/Shared/_BaseLayout.cshtml";
}

<p>@ViewBag.Message</p>
```

先ほど作成した _BaseLayout.cshtml を Layout に設定。

HelloWorldController に ViewBag.Title を追加

```csharp:HelloWorldController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Chrowa3.Controllers
{
    public class HelloWorldController : Controller
    {
        // GET: HelloWorld
        public ActionResult Index()
        {
            return Content("Hello World");
        }

        public ActionResult Bar()
        {
            ViewBag.Message = "Hello View ! Hello Bar !";
            ViewBag.Title = "Bar Title";
            return View();
        }

        public ActionResult Foo(string foo,string bar)
        {
            return Content(foo + " " + bar); ;
        }
    }
}
```

結果

![スクリーンショット 2015-05-17 22.42.38.png](https://qiita-image-store.s3.amazonaws.com/0/9988/0199539d-fa02-3a72-e758-a954e1ec660d.png)

見た目は先ほどの /HelloWorld/Bar と余り変わらないけど html はこういう状態に。

```html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Bar Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    


<p>Hello View ! Hello Bar !</p>



<!-- Visual Studio Browser Link -->
<script type="application/json" id="__browserLink_initializationData">
    {"appName":"Chrome","requestId":"2c4b17abeffa4e29af53a582e90a741f"}
</script>
<script type="text/javascript" src="http://localhost:49504/2b322cb7b4614b37a34871f4fdba20cf/browserLink" async="async"></script>
<!-- End Browser Link -->

</body>
</html>
```



## :octocat: 部分ビュー

ヘッダーとフッターを定義してみるよ

```html:_Header.cshtml
<div id="header">
    <h1>Chrowa3.com</h1>
</div>
```

```html:_Footer.cshtml
<div id="footer">
    <p>copyright 2015 chrowa3.com</p>
</div>
```

_BaseLayout.cshtml をこんな感じに

```html:_BaseLayout.cshtml
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>@ViewBag.Title</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
    @Html.Partial("_Header")
    @RenderBody()
    @Html.Partial("_Footer")
</body>
</html>
```

Html.Partial( ) で引数に View名を指定すると部分ビューが引っ張られる。

![スクリーンショット 2015-05-17 23.17.08.png](https://qiita-image-store.s3.amazonaws.com/0/9988/1cf5e6c8-5d01-ca26-364d-6189c63c5e8c.png)

bootstrap が効いているとわかりにくいなw

# :dizzy: Model

MyProject/Models に Books.cs を作る。いわゆるデータモデルだ。

```csharp:Book.cs
namespace Chrowa3.Models
{
    public class Book
    {
        public int id { get; set; }
        public string Title { get; set; }
        public int Price { get; set; }
        public string ISBN { get; set; }
        public string PublishDate { get; set; }
    }
}
```

Nuget で EntityFramework を突っ込む

![スクリーンショット 2015-05-17 23.52.24.png](https://qiita-image-store.s3.amazonaws.com/0/9988/c1d70f41-cc24-1a81-7746-82981f98e8f4.png)

![スクリーンショット 2015-05-17 23.52.36.png](https://qiita-image-store.s3.amazonaws.com/0/9988/107a8ea7-3c2b-c261-f843-6cf06030e14d.png)

## :octocat: コンテキストクラスを作成

コンテキストクラスはデータモデルとデータベースとの接続の橋渡しをするクラスです。

MyProject/Models に StandardContext.cs を作る。

```csharp:StandardContext.cs
using System.Data.Entity;

namespace Chrowa3.Models
{
    public class StandardContext : DbContext
    {
        public DbSet<Book> Books { get; set; }
    }
}
```

重要な点は以下の 3つ

* System.Data.Entity を using で追加
* DbContext クラスを継承
* DbSet<データモデル>型のプロパティを定義（※）

※ public であることと、単数系のモデル名に対して複数系の名前で定義する

## :octocat: イニシャライザ（initialize:初期化）を作成

データベースの「中身」を入れるためのイニシャライザを用意

```csharp:StandardInitializer.cs
using System;
using System.Collections.Generic;
using System.Data.Entity;

namespace Chrowa3.Models
{
    public class StandardInitializer : CreateDatabaseIfNotExists<StandardContext>
    {
        protected override void Seed(StandardContext context)
        {
            var books = new List<Book> {
                new Book {
                    Title = "C#",
                    Price = 4800,
                    ISBN = "978-4-87311-650-1"
                },
                new Book {
                    Title = "JavaScript",
                    Price = 4536,
                    ISBN = "978-4-87311-573-3"
                },
                new Book {
                    Title = "Ruby",
                    Price = 4104,
                    ISBN = "978-4-87311-573-3"
                }
            };

            books.ForEach(c => context.Books.Add(c));

            context.SaveChanges();
        }
    }
}
```

データベースの初期化処理は以下の様なクラスを継承することで挙動を変えることが出来るよ

| クラス名 | 説明 |
|----------|----------|
| DropCreateDatabaseAlways | アプリケーションを実行する度にデータベースを作成 |
| CreateDatabaseIfNotExists | データベースが存在しない場合にデータベースを作成 |
| DropCreateDatabaseIfModelChanges | モデルが変更された場合にデータベースを作成 |

### :sparkles: Seedメソッドについて

SeedメソッドはCreateDatabaseIfNotExistsクラスやDropCreateDatabaseAlwaysクラスで定義されているprotectedな抽象メソッドだ。

このSeedメソッドをオーバーライドして初期化するデータを入れてあげれば、データベースに反映される

> 実際にコンテキストにデータを追加してシードするためにオーバーライドされるメソッド。 既定の実装では、何も行われません。
　[MSDN : CreateDatabaseIfNotExists<TContext> クラス](https://msdn.microsoft.com/ja-jp/library/gg679221%28v=vs.113%29.aspx)

先ほどのコードでは books に book を登録していっている

```csharp
var books = new List<Book> {
    new Book {
        Title = "C#",
        Price = 4800,
        ISBN = "978-4-87311-650-1"
    },
    new Book {
        Title = "JavaScript",
        Price = 4536,
        ISBN = "978-4-87311-573-3"
    },
    new Book {
        Title = "Ruby",
        Price = 4104,
        ISBN = "978-4-87311-573-3"
    }
}
```

データ定義後は books を ForEachでループし、public DbSet<Book> Books { get; set; } に対して add で追加。最後に SaveChanges() でデータベースに反映させています。

```csharp
books.ForEach(c => context.Books.Add(c));
context.SaveChanges();
```

### :sparkles: イニシャライザの生成と呼び出し

イニシャライザはアプリケーションの起動時に一度呼び出せべばいいので、Global.asax で生成、おそらく Database.SetInitializer 辺りが呼び出しておいてくれるんでしょう。

```csharp:Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

using Chrowa3.Models;
using System.Data.Entity;

namespace Chrowa3
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            Database.SetInitializer<StandardContext>(new StandardInitializer());
        }
    }
}
```

# :dizzy: Web.config に DB接続文字列を追加する

DB は SQLServer ってことでこんな感じで connectionStrings を追加した。

```xml
<configuration>
	<connectionStrings>
		<add name="StandardContext" connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=Chrowa3;User ID=sa;Password=xxxxxxx;" providerName="System.Data.SqlClient" />
	</connectionStrings>
</configuration>
```

確認

![スクリーンショット 2015-05-18 23.42.53.png](https://qiita-image-store.s3.amazonaws.com/0/9988/79d5855f-6262-3389-1a2c-3edf8a374aa6.png)


# :dizzy: まとめ

いわゆるよくある MVC FW で、コントローラー名、アクションメソッド名、アクションメソッドに引き渡すパラメーター、View名など「名前」にかなり依存するデザインとなっている。

最初は慣れないかもしれないが、この「雰囲気」は他の同じようなFWに言っても潰しが利くし、然程難しいルールじゃないってのと、今となっては VisualStudio も無料で手に入れられる時代なので、ASP.NET MVC5 から C# に触れてみるのもありなんじゃないかなぁと。

てゆーか EntityFramework って Nuget でごにょごにょしなきゃダメだったっけw