空のプロジェクトからASP.NET MVC5 EntityFramework6 for Oracleを試す(登録・更新・削除)

# はじめに
「[空のプロジェクトからASP.NET MVC5 EntityFramework6 for Oracleを試す](http://qiita.com/yaju/items/3b1b5fb77ff684d564f9)」の続きで登録・更新・削除を作成していきます。
参考にしたのは、「[ASP.NET MVC5 Scaffolding ことはじめ](http://qiita.com/Chrowa3/items/756abc2452f54df7a088)」となります。

## Scaffoldingとは
「Scaffolding」には足場という意味があります。
アプリケーションのスキャフォールディング (scaffolding) は開発者がオブジェクトの作成、 取得、更新、削除といった基本的なアプリケーションを定義、作成するためのテクニックとなります。

## 参考記事の注意点
この記事通りに作成すれば、SQL Server 2014 Expressで登録・更新・削除が出来るのですが、2点悩んだところがありました。

* 「There was an error running the selected code generator: Try rebuilding the project」のエラーについて
Scaffoldingでプログラムを自動生成するのですが、自動生成前にビルドしておく必要があります。

* 「’EntityState’ は、’System.Data.EntityState’ と ‘System.Data.Entity.EntityState” 間のあいまいな参照です。」の警告
「System.Data.Entity.EntityState.Modified;」を選択する。

# スキャフォールディングの作成
今回はOracleなので前回のプロジェクトをそのまま使用しつつ、[参考記事の見出し「scaffolding」](http://qiita.com/Chrowa3/items/756abc2452f54df7a088)の通りにやっていきます。

「Controllers」フォルダ上で、右クリックをし「追加」、「コントローラー」を選択。
![Add_Controller.png](https://qiita-image-store.s3.amazonaws.com/0/14053/c4ca84ac-be69-acb5-ad7d-44ec7e9260cb.png)

「Entity Framework を使用した、ビューがある MVC コントローラー」を選択。
![Add_Scaffolding.png](https://qiita-image-store.s3.amazonaws.com/0/14053/17a425ad-69b9-b6d9-24e1-f3adef3bd0ff.png)

BookモデルとStandardContextコンテキストを設定します。コントローラ名は「BooksController」にします。前回の「BookListController」と分けるためです。
![Add_Scaffolding2.png](https://qiita-image-store.s3.amazonaws.com/0/14053/0dc4f6c3-8b7a-f51b-55b7-6afe476982b2.png)

これでControllersフォルダに「BooksController」とViewsフォルダに「Books」フォルダが自動生成されます。
![Add_Scaffolding3.png](https://qiita-image-store.s3.amazonaws.com/0/14053/2dc00c96-e4f7-3b57-6f99-17a40700f209.png)

## 自動生成されたソースコード

```csharp:BooksController
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using MvcBasic.Models;

namespace MvcBasic.Controllers
{
    public class BooksController : Controller
    {
        private StandardContext db = new StandardContext();

        // GET: Books
        public ActionResult Index()
        {
            return View(db.Books.ToList());
        }

        // GET: Books/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return HttpNotFound();
            }
            return View(book);
        }

        // GET: Books/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Books/Create
        // 過多ポスティング攻撃を防止するには、バインド先とする特定のプロパティを有効にしてください。
        // 詳細については、http://go.microsoft.com/fwlink/?LinkId=317598 を参照してください。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "id,Title,Price,ISBN,PublishDate")] Book book)
        {
            if (ModelState.IsValid)
            {
                db.Books.Add(book);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(book);
        }

        // GET: Books/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return HttpNotFound();
            }
            return View(book);
        }

        // POST: Books/Edit/5
        // 過多ポスティング攻撃を防止するには、バインド先とする特定のプロパティを有効にしてください。
        // 詳細については、http://go.microsoft.com/fwlink/?LinkId=317598 を参照してください。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "id,Title,Price,ISBN,PublishDate")] Book book)
        {
            if (ModelState.IsValid)
            {
                db.Entry(book).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(book);
        }

        // GET: Books/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return HttpNotFound();
            }
            return View(book);
        }

        // POST: Books/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Book book = db.Books.Find(id);
            db.Books.Remove(book);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
```

# 確認
前回作成したBooksテーブルにはデータが残っているので、SQL Developerを使って一旦Booksテーブルのデータを削除します。
「http://localhost:52955/Books/」にアクセスします。
![Books_Create.png](https://qiita-image-store.s3.amazonaws.com/0/14053/73ac68b2-b821-9ed9-25c7-6dc548e17f14.png)

ちなみにBooksテーブル関連を削除(※注意点を参照)した場合、イニシャライザ(StandardInitializer)クラスのSeedメソッドにて、データベースの中身をセットしているため、最初からデータが登録されている状態となります。
![Books_Create3.png](https://qiita-image-store.s3.amazonaws.com/0/14053/5a1c9283-8350-fbaf-08cc-3feaba5a81f8.png)

## Create New をクリック
![Books_Create2.png](https://qiita-image-store.s3.amazonaws.com/0/14053/cf4bdd15-f572-fca4-c9b4-21a3944a5881.png)


データを入力して、「Create」ボタンをクリックします。
入力したデータが反映されています。
![Books_Index.png](https://qiita-image-store.s3.amazonaws.com/0/14053/f8134138-0205-c509-c478-0b6ef0462463.png)

## Edit (編集)
![Books_Edit.png](https://qiita-image-store.s3.amazonaws.com/0/14053/b35286d6-3bbc-38a7-493f-2319b168e09d.png)

## Details (詳細)
![Books_Details.png](https://qiita-image-store.s3.amazonaws.com/0/14053/4787fa8f-68e8-8875-8bf3-06c31b98f508.png)

## Delete (削除)
![Books_Delete.png](https://qiita-image-store.s3.amazonaws.com/0/14053/8daabf9f-d446-a83d-3c9b-c18f9b8b4f18.png)
「Delete」ボタンをクリックしても確認画面はないので即削除となります。

## テーブル
![Books_Table2.png](https://qiita-image-store.s3.amazonaws.com/0/14053/64ec26a4-8a4e-1ee9-e015-3bfb5eb3a052.png)

### 注意点
標準的なint型のIdを利用している場合、主キーは自動連番となります。
表以外にも索引とトリガーと順序が別途生成されます。
Idを使わない場合、「[主キー項目は自動生成されない](http://www.atmarkit.co.jp/fdotnet/aspnetmvc3/aspnetmvc3_04/aspnetmvc3_04_01.html)」にあるように、そのままでは動作しないので要注意

|種類|名前|
|:------|:------|
|表|Books|
|索引|PK_Books|
|トリガー|TR_Books|
|順序|SQ_Books|

表のみを削除しても他が残っていると、「ORA-00955: すでに使用されているオブジェクト名です。」となって例外エラーとなってしまいます。

```sql:TR_Books
create or replace trigger "TEST"."TR_Books"
before insert on "TEST"."Books"
for each row
begin
  select "TEST"."SQ_Books".nextval into :new."id" from dual;
end;
```
順序:SQ_Books
![Books_Seq.png](https://qiita-image-store.s3.amazonaws.com/0/14053/90f52afd-b64a-6327-3516-9ceb2101b0da.png)

# 今後について
スキャフォールディングの作成は簡単にできることが分かった。実際にビジネスアプリケーションとして使うには、もっと作りこまないといけないけどね。
次は既にテーブルがある既存アプリケーションをASP.NET MVCで再構築する場合、どうやればいいのかについてやっていきたい。