I've been in the guts of an ASP.NET MVC 5 application lately and have some more perspective on the improvements we take for granted in ASP.NET Core.
It is refreshing to work with a framework that embraces dependency injection, like ASP.NET Core. ASP.NET MVC danced around DI and provides a hook for a central dependency resolver, but you always have to wonder if something might not work because you’ve strayed outside the lines.
The artificial separation between ASP.NET MVC and the ASP.NET Web API was unfortunate and unpleasant, but follows Conway’s law.
The startup logic for an ASP.NET MVC application is difficult to follow. There is code in global.asax and three or four other files in the App_Start folder. I haven’t been a big fan of how ASP.NET Core applications organize startup logic (see opinion 7 and 10), but even the worst examples are far better than the MVC approach.
Applications built before attribute routing was popular have the worst API routes.
Front end builds were easy and fast when all you had to do was bundle and minify jQuery and a few other files.
The scaffolding tools in MVC 5 are far ahead of the scaffolding tools for ASP.NET Core. Not only are the older tools considerably faster, but they tend not to throw exceptions as often as the ASP.NET Core tooling. Hopefully, ASP.NET Core will catch up.