どうも、ラルフです

最近 ASP.NET Core を使用してゴリゴリ書いているのですが、2系専用の書き方での見落としのせいで痛い目を見たので後々同じ症状を踏んだ人の助けになればと記していきます。

ASP.NET Core 2系は1系から大きく変わっている

自分の作っているプロジェクトははじめ1系で書いており、2ヶ月くらい前に2系に書き直しました。

公式できちんと移行の手順や変更点が記されているので、移行自体は比較的すんなりできます。

自分が使っていない部分も変わっている

詰まった原因は主にこのせいですが、自分が使っていない部分で新しい書き方に変わっていると、Web上の古い1系の記事のコードを参考にしてきたとき に引っかかります。

特にASP.NETではCore 1系かCore 2系かそれ以外の記事かというのが比較的わかりにくい(個人の感想)ので、新しい書き方をキチンと知っておく必要があります。

まぁ、コンパイルエラーなんかで赤線が引かれればいいですが、引かれず、さらにすぐにエラーが露見しないものだった場合は非常に面倒です。

データ投入コードの変化

今回引っかかったのは、移行手順の中のデータベース初期化コードの移動に関する部分でした。

1系では、Startup.cs の中の Configure メソッド内に初期化コードを書けば大丈夫でした。

1
2
3
4
5
6
7
8
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

SeedData.Initialize(app.ApplicationServices);

しかし、2系では後述の仕組みの変化により、Program.csMain メソッド内に記述するのが正しくなっています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        SeedData.Initialize(services);
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred seeding the DB.");
    }
}
host.Run();

自分のところでは、2系に書き換えてから初期データ投入の必要が出てきたので、正しい書き方を知らず、検索して出てきた1系の手順通りに書いてしまいました。

発生するエラーとそのタイミング

この書き間違いで、すぐにプログラムを起動することができなくなるわけではなく、データベースへのマイグレーションを適用しようとするとき になって初めてエラーが露見します。

エラーは以下のような文章です。

1
relation "AspNetRoles" already exists

自分はPostgreSQLを使用しているため、別のデータベースの場合はメッセージが異なるかもしれません。

なぜエラーが発生するのか

2系への移行手順のページの中に書かれていますが、dotnet ef update database のようなコマンドは、1系では Startup.csConfigureServices メソッドまでしか実行しなかったのが、2系では追加で Configure メソッドまで実行されるようになっています。

初期データ投入用のコードの中で、Database.EnsureCreated のようなメソッドを呼び出すと、その時点でテーブルの作成が行われてしまうため、 Configure メソッドの呼び出しの後に行われる各々のマイグレーションの適用のときに既にテーブルが存在してしまいエラーになる といった感じになっています。

まとめ

バージョン変更でのChangelogや、特に大きな変更があったときの移行マニュアルなんかはきちんと目を通して、正しい情報を覚えておきましょう。

では