MacでEntityFramework7(beta6)経由でsqliteを使う


ASP.NET含め、Mac上で最低限の.NETアプリが動かせるようになりました。
が、結局の所EntityFrameworkのプロバイダが、事実上SQL Serverにしか対応しておらず※、業を煮やしておりましたが、2015年7月27日にリリースされたbeta6でなんとか使えるようになったということなので早速試してみます。

>InmemoryDBやSqliteにも対応をうたっていましたが、事実上動きませんでした（少なくともSqlite版は）。また、MySQLやAzure Tableとかは当分おあずけみたいです。

今回は公開された[サンプルサイト](http://ef.readthedocs.org/en/latest/getting-started/osx.html)のコードを検証してみたいと思います。
EntityFrameworkを利用したCodeFirstのサンプルです。


##結論

動きました！
が、2015年7月30日現在、サンプルサイトの通りやっても動きません。
私が見つけた範囲で、バグが3箇所あり、コードを追加したり、編集したりする必要がありました。
（追記：8月3日現在、コードが修正されているようです）

もちろん、本記事ではちゃんと動くように対応・修正した内容となっています。

##環境構築
サンプルサイトの手順通りか、[私が以前書いた内容](http://qiita.com/zaburo/items/86c21a1b86f526813802)を参考にしてみてください。
ひな形としては、サンプルサイト通り自力で書いても、yeomanを使ってもいいでしょう。yeomanを利用する場合は、Console Applicationを選べばいいです。

前からインストールしている人は、各種環境をbeta6にアップデートする必要があるので、

```bash
dnvm upgrade
dnvm list
```
を実行して、利用環境がbeta6になっていることを確認して下さい。
私の環境では、

```bash
Active Version              Runtime Arch Location             Alias
------ -------              ------- ---- --------             -----
       1.0.0-beta5          mono         ~/.dnx/runtimes
  *    1.0.0-beta6          mono         ~/.dnx/runtimes      default
```
となっています。

あと、当然sqliteが必要です。インストールがまだな人は[ここ](http://qiita.com/zaburo/items/ad690284f7430bdf5f9e)を参考にインストールしたり、簡単な操作を行えるようにしてみてください。

##サンプルコードをなぞってみる

とりあえず作業フォルダは、ConsoleAppとします。

```bash
mkdir ConsoleApp
cd ConsoleApp
```

###project.jsonを用意する

プロジェクトに必要な情報を定義する、project.jsonを用意します。

```js
{
    "dependencies": {
        "EntityFramework.Sqlite": "7.0.0-beta6",
        "EntityFramework.Commands": "7.0.0-beta6"
    },
    "commands": {
    	 "run": "ConsoleApp",
        "ef": "EntityFramework.Commands"
    },
    "frameworks": {
        "dnx451": { },
        "dnxcore50": {
            "dependencies": {
                "System.Console": "4.0.0-beta-*"
            }
        }
    }
}
```
EntityFrameworkの指定は第一階層のdependenciesに設定する必要があります(framework配下にもdependenciesは有りますが）。

この状態で、

```bash
dnu restore
```
を実行し、必要ファイルを取得します。
composer updateみたいなものです。

###アプリケーション本体用コード Program.csを用意する

とりあえずHello dnx!とだけ表示する簡単なコードを準備。

```csharp
// Program.cs
using System;

namespace ConsoleApp
{
    public class Program
    {
        public void Main(string[] args)
        {
            Console.WriteLine("Hello dnx!");
        }
    }
}
```

###ひとまず動作を確認する

####プログラム
まず、先ほどのC#で書いたコードが正常に実行できるか試します。

```bash
dnx . run
```
project.jsonでConsoleAppのところを変更していない場合はdnx . ConsoleAppとします。

>Hello dnx!

と表示されればOKです。

####EntiryFrameworkのコマンド
次に、モデルやMigrationファイルの生成に使うEntityFrameworkのコマンド群が機能するかテストしておきます。

```bash
dnx . ef
```
とすると、

```bash
                     _/\__
               ---==/    \\
         ___  ___   |.    \|\
        | __|| __|  |  )   \\\
        | _| | _|   \_/ |  //|\\
        |___||_|       /   \\\/\\

 7.0.0-beta6-13815

Usage: ef [options] [command]
```
こういう感じのが表示されればOKです。
うまく動かない場合は、project.jsonのdependenciesでEntityFramework.Commandsが設定されているかチェックして下さい。

なお、efという命令はproject.jsonの中の、commandsで定義した名前です。別にefじゃなくてもいいです。

##ここからが本題
基本的な動作が確認できたので、DB（EF）を使うコードを書いていきます。

まず、クラスとDBを仲介（情報を提供）したり、Migrationファイル生成の元となるModel.csを作ります。

```csharp
using Microsoft.Data.Entity;
using System.Collections.Generic;

namespace ConsoleApp
{
	public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=./blog.db");
        }
    }
	
	public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
        public string Name { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}
```

###一度ビルドしてみる

```bash
dnu build --quiet
```
を事項し、Warning(s)やError(s)が0であることを確認しましょう。

```bash
Building ConsoleApp for DNX,Version=v4.5.1
Building ConsoleApp for DNXCore,Version=v5.0

Build succeeded.
    0 Warning(s)
    0 Error(s)
```
こんな感じになればOK。

###Migrationファイルを作る（バグ１）
efコマンドを利用して、Model.cs情報に基いてテーブルを生成するためのmigrationファイルを生成します。

```bash
dnx . ef migration add MyFirstMigration
```
実行すると、エラーを吐きます。

```bash
System.InvalidOperationException: A type named 'StartupProduction' or 'Startup' could not be found in assembly 'ConsoleApp'.
・
・
・
```
Startupが無いぞ！と怒られているようなので、Statup.csを作成します。
サンプルサイトには、どこにもそんなことは書いていませんが。

```csharp
using Microsoft.AspNet.Builder;

namespace ConsoleApp
{
    public class Startup
    {
		public void Configure(IApplicationBuilder app)
	    {
	    } 
    }
}
```

再度、migrationの生成を実行します。

```bash
dnx . ef migration add MyFirstMigration
Using context 'BloggingContext'.
Writing migration to '/Users/tamaki/ConsoleApp/Migrations/20150730020129_MyFirstMigration.cs'.
Writing model snapshot to '/Users/tamaki/ConsoleApp/Migrations/BloggingContextModelSnapshot.cs'.
Done. To undo this action, use 'ef migration remove'.
```
という感じで、Migrationsフォルダが作成され、その下にmigration用のファイルが生成されます。

###Migrationしてみる（バグ２）

では、生成されたMigrationファイルを元に、DBを作成してみます。

```bash
dnx . ef migration apply
```

すると、またエラーを吐きます。

```bash
Microsoft.Framework.Runtime.Roslyn.RoslynCompilationException: /Users/tamaki/ConsoleApp/Migrations/20150730020129_MyFirstMigration.cs(17,61): DNX,Version=v4.5.1 error CS1503: Argument 2: cannot convert from 'bool' to 'string'
・
・
・
```
どうやら、xxxx_MyFirstMigration.csで設定されているコードにおいてboolからstringに変換できない！と怒っているようです。

というわけで、xxxx_MyFirstMigration.csのコードを編集します。
BlogテーブルとPostテーブルのIdの設定箇所がおかしいようです。

16行あたりを、

```csharp
BlogId = table.Column(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
```
から、

```csharp
BlogId = table.Column(type: "INTEGER", nullable: false),
```

と修正します。.Annotation("Sqlite:Autoincrement", true)を削除します。
さらに29行目あたりを同様に修正します。

```csharp
PostId = table.Column(type: "INTEGER", nullable: false)
                        .Annotation("Sqlite:Autoincrement", true),
```
を、

```csharp
PostId = table.Column(type: "INTEGER", nullable: false),
```

とします。

再度、migrationを実行します。

```bash
dnx . ef migration apply
Using context 'BloggingContext'.
Using database 'main' on server './blog.db'.
Applying migration '20150730020129_MyFirstMigration'.
Done.
```

Done.となればOKです。

同じ階層にblog.dbファイルが生成されているはずです（Model.csで定義したので）。

###DBを利用してみる（バグ３）

Program.csを下記のようにDBを使うように編集します。

URLをDBに書込、それを読みだしているようです。
サンプルコードには簡単な？バグがありましたが、下記は修正済みです。

```csharp
using System;

namespace ConsoleApp
{
    public class Program
    {
        public void Main(string[] args)
        {
            using (var db = new BloggingContext())
            {
                db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet/hoge" });
                var count = db.SaveChanges();
                Console.WriteLine("{0} records saved to database", count);

                Console.WriteLine();
                Console.WriteLine("All blogs in database:");
                foreach (var blog in db.Blogs)
                {
                    Console.WriteLine(" - {0}", blog.Url);
                }
            }
        }
    }
}
```

サンプルコードではインスタンス生成はインスタンス名contextとなっているのに、利用時はdbとなっています。

###実行してみる

```bash
dnx . run
1 records saved to database

All blogs in database:
 - http://blogs.msdn.com/adonet/hoge
```
という感じで、1行表示されています。

###一応sqlite側でも確認してみる

```bash
sqlite3 blog.db
SQLite version 3.8.5 2014-08-15 22:37:57
Enter ".help" for usage hints.
sqlite>
sqlite> .table
Blog                Post                __migrationHistory
sqlite> select  * from Blog;
1||http://blogs.msdn.com/adonet/hoge
sqlite>
```
テーブルが作成され、ちゃんと保存されているようです。

##最後に

大変でした。
asp.net mvc6からもそのうち試してみたいと思います（[やってみました](http://qiita.com/zaburo/items/96afc69a39469ad68864)）。
とはいえとりあえずControllerから叩けばいいだけかな。