OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

ASP.NET Core × Entity Framework × SQLite

ちょいメモ。
ASP.NET CoreでEntityFrameWorkでSQLiteを使えるようにするところまで。

プロジェクトの準備

Visual Studio 2017から、今回は「ASP.NET Core Web API」としてプロジェクト作成。
.NET Core 2.0。

NuGetから以下のパッケージをインストールしておく。
・Microsoft.EntityFrameworkCore.Tools
・Microsoft.EntityFrameworkCore.Sqlite

テーブル準備

DbContextを継承したクラスを作成する。
この中でOnConfiguringをオーバーライドして、SQLiteデータファイルへの接続文字列を指定する。
SQLiteの接続文字列はとてもシンプルだ。
「data.db」というファイルをドキュメントルート直下に置く場合は以下のように。

public class MyDbContext : DbContext  
{  
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
    {  
        base.OnConfiguring(optionsBuilder);  

        optionsBuilder.UseSqlite(@"Data Source='data.db'");  
    }  
}  

ここではID, 年齢, 名前だけのPersonテーブルを作るとしましょう。
まず、そのレコードを格納するためのデータクラスを用意する。コードファースト。

public class Person  
{  
    public int Id { get; set; }  
    public int Age { get; set; }  
    public string Name { get; set; }  
}  

コンテキストクラスに、対応するDbSetプロパティを追加。

public class MyDbContext : DbContext  
{  
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
    {  
        base.OnConfiguring(optionsBuilder);  

        optionsBuilder.UseSqlite(@"Data Source='data.db'");  
    }  

    public DbSet<Person> Persons { get; set; }  
}  

ここまでコードを作ってから、Microsoft.EntityFrameworkCore.Toolsを利用して、マイグレーションでテーブルを作成する。
Visual Studioのパッケージ・マネージャー・コンソールから、以下コマンドを実行。

PM> Add-Migration InitPersonTable  
PM> Update-Database  

(「InitPersonTable」の部分は任意の名前)

Migrationsフォルダ配下のマイグレーション管理クラスと、SQLiteのデータファイル data.db が生成される。

テーブル読み書き

さて、ASP.NET Core Web APIのプロジェクトテンプレートには、以下のようなサンプルのコントローラクラスが用意されているかと思います。

[Route("api/[controller]")]  
public class ValuesController : Controller  
{  
    // GET api/values  
    [HttpGet]  
    public IEnumerable<string> Get()  
    {  
        return new string[] { "value1", "value2" };  
    }  

    // GET api/values/5  
    [HttpGet("{id}")]  
    public string Get(int id)  
    {  
        return "value";  
    }  

    // POST api/values  
    [HttpPost]  
    public void Post([FromBody]string value)  
    {  
    }  

    // PUT api/values/5  
    [HttpPut("{id}")]  
    public void Put(int id, [FromBody]string value)  
    {  
    }  

    // DELETE api/values/5  
    [HttpDelete("{id}")]  
    public void Delete(int id)  
    {  
    }  
}  

これを使って、getとpostを試してみましょう。

まずは、何もいじらずに立ち上げてみる。

Visual StudioからそのままIIS Expressでデバッグ起動でも良いけど、一応デプロイ用のファイルをローカルフォルダに出力してみる。
ソリューションエクスプローラーからプロジェクト右クリック > [発行] > [フォルダー]選んでパス入力 > [発行]

しかしこのままだとさっきのdata.dbが発行先にコピーされない。
これは自分でdata.db右クリック > [プロパティ] > [出力ディレクトリにコピー] で設定すんのかな。

発行されたら、dotnetコマンドで起動。

>dotnet プロジェクト名.dll  

ブラウザから http://localhost:5000/api/values/1234 にアクセスしてみる。
サンプルのままなら、なんのIDを指定しても固定で「value」だけ表示されるはず。

では、ちゃんとPersonテーブルから名前を返しましょう。
IDを引数にとるgetメソッドを以下のように書き換える。

[HttpGet("{id}")]  
public IActionResult Get(int id)  
{  
    using (var context = new MyDbContext())  
    {  
        var person = context.Persons.FirstOrDefault(p => p.Id == id);  
        if (person == null) return NotFound();  

        return new ObjectResult(person) as IActionResult;  
    }  
}  

もっかい立ち上げなおしてアクセスしてみる。
テーブルにはまだレコードがないので、404 NotFoundのページになればOK。

では、POSTでレコードを登録しましょう。

まずPostメソッドを以下のように書き換える。

[HttpPost]  
public IActionResult Post([FromBody]Person person)  
{  
    if (person == null) return BadRequest();  

    using (var db = new MyDbContext())  
    {  
        db.Persons.Add(person);  
        db.SaveChanges();  
    }  

    return CreatedAtRoute("values", new { id = person.Id }, person);  
}  

Postの応答ではstatus 201で、新しく登録されたリソースへのルートを返すのが良いらしいので、上の例ではそのようにしている。
CreatedAtRouteの引数でvaluesというルート名を指定してるけど、この名前はGETメソッドのほうにつけておかないといけない。

[HttpGet("{id}", Name ="values")]  
public IActionResult Get(int id)  
{  
    (省略)  
}  

呼び出し例

では、このAPIの呼び出しも別アプリとして作ってみましょう。
適当なコンソールアプリで↓こんなのを。詳しくは HttpClientでJSONデータをPOSTする をどうぞ。

var person = "{ \"Id\" : 123,  \"Age\" : 20, \"Name\" : \"鈴木一郎\"  }";  

using (var client = new HttpClient())  
{  
    var content = new StringContent(person, Encoding.UTF8, "application/json");  
    var res = client.PostAsync("http://localhost:5000/api/values", content).Result;  

    Console.WriteLine(res);  
}  

結果。

StatusCode: 201, ReasonPhrase: 'Created', Version: 1.1, Content: System.Net.Http.NoWriteNoSeekStreamContent, Headers:  
{  
  Date: Mon, 12 Feb 2018 10:00:33 GMT  
  Transfer-Encoding: chunked  
  Location: http://localhost:5000/api/Values/123  
  Server: Kestrel  
  Content-Type: application/json; charset=utf-8  
}  

とりあえずここまで。

参考:
EntityFrameworkCoreを.NET Core コンソールアプリでCodeFirstに使う
ASP.NET Core と Visual Studio for Windows で Web API を作成する