ASP.NET MVC + MySQL で開発環境構築

ASP.NET MVC & EntityFramework によるコードファースト開発環境を MySQL で構築してみます。
と言っても今回はプロジェクトを作成して ASP.NET Identity によるユーザー認証を MySQL で動かすところまで。

(2015.10.22 追記)
Visual Studio 2015 + .NET Framework 4.6 でも動作確認。
現在 beta 8 が公開中の ASP.NET 5 (ASP.NET MVC 6) は、まだ MySql.Data.Entity が対応していないので MySQL は使用できません。
(追記ここまで)

#準備
Visual Studio 2013 Community または Professional 以上を用意。
Express for Web では拡張機能が使えないので注意。

#プロジェクト作成
新規作成 -> プロジェクト -> Visual C# -> ASP.NET Webアプリケーション を選択。
.NET Framework 4, 4.5 および 4.5.1 のサポートは 2016年1月12日 に終了するらしいので .NET Framework 4.5.2 を推奨。

テンプレートは MVC を選択して、認証に「個人ユーザーアカウント」を使用。
今回は「単体テストの追加」とAzureの「クラウド内のホスト」のチェックは外しておきます。
![WS0016.jpg](https://qiita-image-store.s3.amazonaws.com/0/57132/c0b0e7d6-f2f9-3404-015f-b7228820ff12.jpeg)

これで ASP.NET アプリケーションが新規作成されました。
デバッグ実行すれば、ユーザー登録・ログインといった ASP.NET Identity の認証機能を使用できることが確認できます。
![WS0017.jpg](https://qiita-image-store.s3.amazonaws.com/0/57132/968e7d2d-e9c8-1f8b-8e91-9335ddd91c32.jpeg)

#MySQL接続
デフォルトでは SQL Server Compact が使用されているので、接続先を MySQL に変更してみます。

##MySql.Data.Entityのインストール・設定
「NuGet パッケージの管理」から MySQL で検索して `MySql.Data.Entity` をインストールします（2015/4/14時点の最新版は6.9.6）。
ついでに Microsoft ASP.NET MVC と EntityFramework も最新版にアップデートしておきます。

次に `Web.config` を開きます（Viewsディレクトリ内の `Web.config` ではなくプロジェクト直下のほう）。
`entityFramework` および `system.data` の設定は `MySql.Data.Entity` のインストール時に自動で追加されているので、`connectionStrings` のみ変更します。

```xml:Web.config
<connectionStrings>
    <!-- <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-webapp-****.mdf;Initial Catalog=aspnet-webapp-****.;Integrated Security=True" providerName="System.Data.SqlClient" /> -->
    <add name="DefaultConnection" connectionString="server=127.0.0.1;port=3306;database=test;uid=root;password=;charset=utf8" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
```

ここではXAMPP上で初期設定の MySQL を想定していますので、接続文字列のパラメータは環境に合わせて変更してください。

##マイグレーション
続いて、マイグレーションを実行して認証で使用されるテーブルをコードから自動生成します。

###Enable-Migrations
「パッケージマネージャー コンソール」から次のコマンドを実行します。
`Enable-Migrations`

Migration ディレクトリと `Configuration.cs` ファイルが作成されました。


次に Add-Migration を実行するのですが、このまま実行すると以下のエラーが発生してしまいます。

`プロバイダー 'MySql.Data.MySqlClient' で MigrationSqlGenerator が見つかりませんでした。対象の移行構成クラスで SetSqlGenerator メソッドを使用して、追加の SQL ジェネレーターを登録してください。`

なので先ほど生成された `Configuration.cs` に、`DbConfiguration` の継承クラスを追加して MigrationSqlGenerator を登録します。

```csharp:Configuration.cs
using MySql.Data.Entity

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
	...
	
	public class MysqlConfiguration : DbConfiguration
	{
		public MysqlConfiguration()
		{
			SetMigrationSqlGenerator(MySqlProviderInvariantName.ProviderName, () => new MySqlMigrationSqlGenerator());
		}
	}
}
```
これで Add-Migration が実行できるようになりました。

###Add-Migration
「パッケージマネージャー コンソール」から次のコマンドを実行します。
`Add-Migration Initial`

Migration ディレクトリ内に `2015****_Initial.cs` が作成されました。
このファイルにコードから生成されたテーブルのカラム情報が保存されています。


最後に Update-Database を実行するのですが、このまま実行すると以下のエラーが発生してしまいます。

`Specified key was too long; max key length is 767 bytes`

これは MySQL ではキーの長さの上限が 255 (utf-8のvarchar型)のためなので、キーとなるカラムの長さを 256 から 255 に修正します。

```csharp:2015****_Initial.cs
public override void Up()
{
    CreateTable(
        "dbo.AspNetRoles",
        c => new
            {
                Id = c.String(nullable: false, maxLength: 128, storeType: "nvarchar"),
                // maxLength: 256 -> 255
                Name = c.String(nullable: false, maxLength: 255, storeType: "nvarchar"),
            })
        .PrimaryKey(t => t.Id)
        .Index(t => t.Name, unique: true, name: "RoleNameIndex");
    
    ...
    
    CreateTable(
        "dbo.AspNetUsers",
        c => new
            {
                Id = c.String(nullable: false, maxLength: 128, storeType: "nvarchar"),
                Email = c.String(maxLength: 256, storeType: "nvarchar"),
                EmailConfirmed = c.Boolean(nullable: false),
                PasswordHash = c.String(unicode: false),
                SecurityStamp = c.String(unicode: false),
                PhoneNumber = c.String(unicode: false),
                PhoneNumberConfirmed = c.Boolean(nullable: false),
                TwoFactorEnabled = c.Boolean(nullable: false),
                LockoutEndDateUtc = c.DateTime(precision: 0),
                LockoutEnabled = c.Boolean(nullable: false),
                AccessFailedCount = c.Int(nullable: false),
                // maxLength: 256 -> 255
                UserName = c.String(nullable: false, maxLength: 255, storeType: "nvarchar"),
            })
        .PrimaryKey(t => t.Id)
        .Index(t => t.UserName, unique: true, name: "UserNameIndex");
    
    ...
}
```
もう一箇所、マイグレーション履歴を保持する `__migrationhistory` テーブルも修正する必要があります。
先ほどの `Configuration.cs` に `HistoryContext` の継承クラスを作成してキーの長さを変更します。

```csharp:Configuration.cs
public class MysqlConfiguration : DbConfiguration
{
	public MysqlConfiguration()
	{
		SetMigrationSqlGenerator(MySqlProviderInvariantName.ProviderName, () => new MySqlMigrationSqlGenerator());
		
		SetHistoryContext("MySql.Data.MySqlClient", (dbConnection, defaultSchema) => new MysqlHistoryContext(dbConnection, defaultSchema));
	}
	public class MysqlHistoryContext : HistoryContext
	{
		public MysqlHistoryContext(DbConnection dbConnection, string defaultSchema)
			: base(dbConnection, defaultSchema)
		{
		}

		protected override void OnModelCreating(DbModelBuilder modelBuilder)
		{
			base.OnModelCreating(modelBuilder);

			modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(255).IsRequired();
			modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(255).IsRequired();
		}
	}
}
```

これで Update-Database が実行できるようになりました。

###Update-Database
「パッケージマネージャー コンソール」から次のコマンドを実行します。
`Update-Database`


以上で MySQL にデータベースが生成されます。
![WS0020.jpg](https://qiita-image-store.s3.amazonaws.com/0/57132/17f48e04-fcd3-3393-8089-33308638e815.jpeg)



デバッグ実行してユーザー登録すれば `aspnetusers` テーブルが更新されることが確認できます。