🐥note.

小鳥とMicrosoft <3 なエンジニアの技術Blog📚

.NET Core 3.0の新機能Roll Forwardオプションを試してみる

.NET Core 3.0 リリース記念 C# Tokyoで登壇された@nuits_jpさんの.NET Core 3.0のPublish Single File概要を見て、そういえば.NET Core 3.0の新機能でRuntimeのバージョンをコントロールする機能あったなぁ…と思い、ちょっと調べてみました。

Microsoft Docsでいうと、このあたりの話です。

docs.microsoft.com

RollForwardとは?

.NET Coreアプリの実行時に使用するRuntimeバージョンは.runtimeconfig.jsonで指定されたFrameworkバージョンを元にフォワードされます。このフォワードされるルールをコントロールするというオプションがRollForwardのようです。

実際に試してみる

インストール済み SDK の確認

下記コマンドでインストール済みのSDKを表示できます。

dotnet --list-sdks

私の環境ではこんな感じでした。

> dotnet --list-sdks
2.1.402 [C:\Program Files\dotnet\sdk]
2.1.602 [C:\Program Files\dotnet\sdk]
2.1.801 [C:\Program Files\dotnet\sdk]
2.2.101 [C:\Program Files\dotnet\sdk]
2.2.105 [C:\Program Files\dotnet\sdk]
2.2.202 [C:\Program Files\dotnet\sdk]
2.2.401 [C:\Program Files\dotnet\sdk]
3.0.100-preview7-012821 [C:\Program Files\dotnet\sdk]
3.0.100 [C:\Program Files\dotnet\sdk]

作成アプリのバージョン指定

.NET Core のプロジェクトを作成します。
使用するSDKのバージョンはあえて古いバージョンで作成します。

mkdir RFSample
cd RFSample
dotnet new global

生成されたglobal.jsonを以下の内容に変更します。

{
  "sdk": {
    "version": "2.1.402"
  }
}

アプリの作成

以下のコマンドでコンソールアプリケーションを作成します。

dotnet new console

コードはこんな感じです

using System;

namespace sample
{
    class Program {
        static void Main (string[] args) {
            Console.WriteLine(typeof(Object).Assembly.Location);
        }
    }
}

dotnet runで実行すると、私の環境ではこんな感じになります。

> cd .\bin\Debug\netcoreapp2.1
> dotnet RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.13\System.Private.CoreLib.dll

global.jsonSDKのバージョンは2.1.402を指定しましたが、実行時のRuntimeは2.1.13を使用しているっぽいですね。

ちなみにインストール済み Runtime の一覧はdotnet --list-runtimesで確認できます。

> dotnet --list-runtimes
Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
... 途中割愛 ...
Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

--roll-forward を使ってみる

Microsoft Docs によると RollForward の指定は複数の方法でできるようです。

  • プロジェクト ファイルのプロパティ: RollForward
  • ランタイム構成ファイルのプロパティ: rollForward
  • 環境変数: DOTNET_ROLL_FORWARD
  • コマンドライン引数: --roll-forward

ここでは一番簡単なコマンド引数の--roll-forwardを使用して実行してみます。

まずはLatestMajorから。

> dotnet --roll-forward LatestMajor .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0\System.Private.CoreLib.dll

使用する Runtime が2.1.13から3.0.0に変わりましたね!
色々試してみましょう。

> dotnet --roll-forward LatestPatch .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.13\System.Private.CoreLib.dll

> dotnet --roll-forward Minor .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.13\System.Private.CoreLib.dll

> dotnet --roll-forward LatestMinor .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.7\System.Private.CoreLib.dll

> dotnet --roll-forward Major .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.13\System.Private.CoreLib.dll

> dotnet --roll-forward LatestMajor .\RFSample.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0\System.Private.CoreLib.dll

> dotnet --roll-forward Disable .\RFSample.dll
It was not possible to find any compatible framework version
The specified framework 'Microsoft.NETCore.App', version '2.1.0' was not found.
  - The following frameworks were found:
      2.1.4 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.1.9 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.1.12 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.1.13 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.2.0 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.2.3 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.2.5 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.2.6 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      2.2.7 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      3.0.0-preview7-27912-14 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
      3.0.0 at [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

You can resolve the problem by installing the specified framework and/or SDK.

The .NET Core frameworks can be found at:
  - https://aka.ms/dotnet-download

変化をちゃんと網羅できていませんが、まぁこんなもんでしょう。

Disable2.1.0のRuntimeがないので怒られてしまいましたね。
Disable一般的な用途としては非推奨なのでテスト用途に使ってね、らしいのです。

.NET Core 2.1で作ったMVCアプリを.NET Core 3.0にRollForwardしたらどうなるのか?

当たり前ですが動作しません。

以下は.NET Core 2.1で作ったASP.NET Core MVC--roll-forward LatestMajorを指定することで強制的に.NET Core 3.0で環境で動かしたログです。

> dotnet --roll-forward LatestMajor MVCSample.dll
Application startup exception: System.InvalidOperationException: Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use 'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside 'ConfigureServices(...).
   at Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.UseMvc(IApplicationBuilder app, Action`1 configureRoutes)
   at MVCSample.Startup.Configure(IApplicationBuilder app, IHostingEnvironment env) in C:\Users\piyosi\work\RFSample\MVCSample\Startup.cs:line 55
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.WebHost.BuildApplication()
crit: Microsoft.AspNetCore.Hosting.WebHost[6]
      Application startup exception
System.InvalidOperationException: Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use 'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside 'ConfigureServices(...).
   at Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.UseMvc(IApplicationBuilder app, Action`1 configureRoutes)
   at MVCSample.Startup.Configure(IApplicationBuilder app, IHostingEnvironment env) in C:\Users\piyosi\work\RFSample\MVCSample\Startup.cs:line 55
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.WebHost.BuildApplication()
Unhandled exception. System.InvalidOperationException: Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use 'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside 'ConfigureServices(...).
   at Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.UseMvc(IApplicationBuilder app, Action`1 configureRoutes)
   at MVCSample.Startup.Configure(IApplicationBuilder app, IHostingEnvironment env) in C:\Users\piyosi\work\RFSample\MVCSample\Startup.cs:line 55
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.WebHost.BuildApplication()
   at Microsoft.AspNetCore.Hosting.WebHost.StartAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String startupMessage)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token, String startupMessage)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host, CancellationToken token)
   at Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
   at MVCSample.Program.Main(String[] args) in C:\Users\piyosi\work\RFSample\MVCSample\Program.cs:line 18

ディスコンされた関数を使用している場合もきっと同じでしょう。

おわり

バグ修正された新しいバージョンで動作させたいとか、そんな感じのシチュエーションで使用するのでしょうか?

個人的には--self-containedやら/p:PublishSingleFileでRuntimeを同封して配布することが多いので、すぐに使う機会は少ないかなぁ、といった感想です。

以上です。