これは既に本番環境で動作している ASP.NET MVC アプリケーションを、軽い気持ちで ASP.NET Core MVC にマイグレーションしようとした考えが甘い男の、真実の物語である。
ネタ記事っぽいですが、割と真面目にマイグレーションしようとして、余りにも辛くなって諦めました。
それでは、まずマイグレーションしようとしたアプリケーションについて、簡単に紹介します。
アプリケーションの概要
既に本番環境で動作しているアプリケーションでしたが、構成は一般的な ASP.NET MVC 5 と Entity Framework 6 を使っているだけの、シンプルな感じです。
- ASP.NET MVC 5
- Entity Framework 6
追加のパッケージとして NuGet からいくつか入れていましたが、代表的なものは以下のような感じです。
- HtmlAgilityPack
- WindowsAzure.Storage
そして実行環境としては Azure を使っています。しかし、WebJobs が既に 4,5 個動作していました。
- Azure Web Apps + WebJobs
大した規模ではなさそうですが、このような規模でも結果として挫折してしまいました。
発生した問題と行った修正
新しく ASP.NET Core MVC アプリケーションを作成し、そこにコードをコピーしてからが実際のマイグレーション作業が始まります。具体的には以下のような問題が発生したので、対応を行いました。
ASP.NET 関係
- フォーム認証に関係するクラスが無い
- Request.IsAuthenticated が無い
- User.Identity.IsAuthenticated に置き換える
- HttpNotFound, HttpStatusCodeResult が無い
- NotFound / StatusCode メソッドを利用
- OutputCache が無い
- ResponseCache を使うか、データキャッシュに実装しなおす
NuGet 関係
- HtmlAgilityPack がインストール出来ない
- .NET Core に対応した HtmlAgilityPack.NetCore を代わりにインストール
- WindowsAzure.Storage がインストール出来ない
- frameworks の imports に portable-net45+win8+wp8+wpa81 を追加
- Facebook がインストールできない
- frameworks の imports に portable-win81+wpa81 を追加
その他
- WindowsAzure.Storage の同期 API が無い
- 非同期 API を使う
- Attribute.GetCustomAttributes メソッドが無い
- Assembly.GetCustomAttribute<T> メソッドを使う
- project.json で参照したプロジェクトのクラスが見えない(ように表示される)
- ReSharper が対応していなかったので Suspend に
ちなみに Razor の非互換を心配していましたが、利用していた範囲では修正が必要なくコピーするだけで問題なかったです。ただし Tag Helpers に書き換えたい気持ちが出てきます。
挫折した理由
Razor の基本クラス周り
MVC 5 までは WebViewPage クラスを継承したクラスを用意し、Web.config の pageBaseType にクラス名を設定して、ユーザー情報などページ全体で使う情報を Razor から扱えるようにしていました。
しかし、Core MVC では RazorPage クラスの継承ではなく、Dependency Injection を使って解決するべきでした。@inherits が用意されているので、無駄に時間を掛けてしまいました。
Display Mode が無い
Core MVC の RTM には Display Mode が実装されませんでした。さらに Request.Browser.IsMobileDevice といった判別するための方法も用意されていないので、自前で実装する必要があります。
レスポンシブ以外で PC とスマートフォンに対応するのは、現状では時間がかかりそうです。
Entity Framework Core の非互換
元々が Entity Framework 6 の Code First を使っていたので、ここに関しては簡単に対応できるのではないかと思っていましたが、その目論見は残念ながら外れてしまいました。
- FindAsync メソッドが無い
- プライマリキーで取得する場合は FirstOrDefaultAsync を使う
- SqlQuery メソッドが無い
- DbSet<T>.FromSql メソッドを使う
- ベタに SQL を書いていた時には諦めるしかない気がする
特にパフォーマンスを優先して SqlQuery を書いていた部分が致命的でした。
Entity Framework Core では FromSql と LINQ と組み合わせて書けるようになっているので、最初から考慮しておけば問題なかったのではないかと思います。
そもそも MVC 5 と仕組みが異なる
もっと早く気が付けという感じですが、ユーザー認証や設定ファイルの仕組みが ASP.NET MVC 5 と ASP.NET Core MVC 1.0 で全く異なっている部分の対応が、どうしようもなく大変でした。
特に今回のアプリケーションは ASP.NET Identity を使っていなかったので、ログイン中のユーザーを扱うための方法がまったく確立されておらず、さらに既にある実装に引きずられてしまい完全に絶望しました。
そして元のアプリケーションが Dependency Injection を使っていなかったことが、さらに追い打ちをかけた感じでした。結局のところ、コンパイルエラーを取り除くことが出来ないまま終わりました。
まとめ
ここまでのコストを支払って、既に稼働しているアプリケーションを Core MVC にマイグレーションする必要はあるのか、と考えると正直なところ今は無いと思いました。
しかし、新規に開発を行うのであれば Dependency Injection や Razor の Tag Helpers や View Components など、最初からあるものとして開発が出来るので、Core MVC を使っても良いかなと思いました。
今回のマイグレーションは失敗しましたが経験を得ることが出来たので、次に新規で開発する時には Core MVC を無理やりにでも選択してみようと思っています。