asp.net-core.net-core

Determine which .NET Core runtime is needed


I found some interesting articles about the difficulties of .NET Core SDK/runtime/tooling versioning, for example:

However, I still don't really know how to deal with all of this in practice:

  1. Given a project with a number of .NET Core dependencies. How can I determine which version of the runtime needs to be available on the end-users machine?
  2. Does the runtime version need to match exactly, or can the runtime installed on the end-users machine be newer than the required version?
  3. Let's say I want to stick to some LTS version of the runtime. How can I determine the version of the packages I need to reference? How can I make sure that no newer packages are referenced?

Oh, and there is one more:

  1. Once I know which runtime version is required on the end-users machine, how can I determine (programmatically) if that version of the runtime (or a newer, backwards compatible one) is available?

Solution

  • First, let's look at what happens when a portable .NET Core application is run via dotnet yourapp.dll:

    Currently (.NET Core 1.0), the framework resolver will use the latest patch version available for the major and minor version specified in the runtimeconfig.json, but no version lower than the runtimeconfig.json specifies. E.g., a 1.1.2 runtime will be used if the runtime config specifies 1.1.1, but if on only 1.1.0 is available, it will log an error. There also isn't any version roll-forward across minor versions. So an application with a runtime configuration set to 1.0.0 will trigger an error if only any 1.1.* is installed.

    For .NET Core 2.0, a minor version roll-forward is planned in case there isn't any matching minor version found. If a 1.0.5 and 1.1.2 runtime are installed, an application with a runtime configuration of 1.0.4 will be run on the 1.0.5 runtime. If only 1.1.2 is installed, the same application will be run on 1.1.2. If only 2.0.0 is installed, the same application will not be able to run. See GitHub issue for .NET Core 2+ Version Binding for details and discussion about this change.

    Let's look at where the value in the runtime configuration comes from. When you target the framework netcoreapp1.1, the tooling you use will determine:

    In the csproj file, the version of the framework to use is determined by the property

    <RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion>
    

    If this value is not specified, the tooling will use the newest version it knows about for .NET Core 1.0 and 1.1.

    For .NET Core 2.0, portable applications will use the patch version 0 by default and self-contained applications will use the latest version that the tooling knows about. This change is being made because tooling (CLI "SDK" / Visual Studio) updates and runtime updates have been released at the same time so apps would require the new runtime to be installed on target systems by default. If that runtime was not installed, an error would occur. This was bad if it takes a few days for hosters to catch up with testing and installing updates. The version can still be enforced / required by setting <RuntimeFrameworkVersion>explicitly.

    About packages: The packages that 1.* tooling uses are meta-packages. So referencing Microsoft.NETCore.App or NETStandard.Library would pull in a lot of other NuGet packages. This is no longer the case for .NET Core 2.0 and .NET Standard 2.0 - the packages are flat and contain everything you need. Also, when you create a NuGet package, those packages will no longer be dependencies of the resulting package. They are used for compilation references only, with the exception of Microsoft.NETCore.App knowing which additional packages to pull in for self-contained applications.

    Previously, a library built with NETStandard.Library version 1.6.1 would cause consuming .NET Core 1.0 applications to contain a lot of updated DLL files that are actually part of .NET Core 1.1. I do not know if this means that LTS policies will cover or not cover applications that end up with those DLLs. And it is hard to see which .NET Core version they belong to since the package versions they originate from are usually 4.0.*, 4.1.* and 4.3.*.

    For ASP.NET Core packages, it is a lot easier since they are versioned 1.0.* and 1.1.* so you can see which "branch" they originate from and you have more control over the versions used by specifying the NuGet packages in the csproj file.

    To recap, let's get back to the original questions:

    1. Given a project with a number of .NET Core dependencies. How can I determine which version of the runtime needs to be available on the end-users machine?

    The real dependency here is which version of Microsoft.NETCore.App is written to the yourapp.runtimeconfig.json file. A version of the same major and minor number and same or higher patch number has to be installed, the latest patch version will be used. When the .NET Core 2.0 resolver is installed, alternatively the highest version with the same major number will be used instead, but a version of the same major and minor number will be preferred.

    If only runtimes with newer major versions are installed, the application cannot be run on the target system (e.g., 1.0.5 app and only 2.0.0 runtime).

    1. Does the runtime version need to match exactly, or can the runtime installed on the end-users machine be newer than the required version?

    The version of the runtime configuration is a hard minimum. For choosing the right version of newer runtimes see above.

    1. Let's say I want to stick to some LTS version of the runtime. How can I determine the version of the packages I need to reference? How can I make sure that no newer packages are referenced?

    The version of Microsoft.NETCore.App will automatically be inferred from the target framework (e.g. netcoreapp1.0 => 1.0.*, patch version depending on the version of the tooling you use). To override the version, set the <RuntimeFrameworkVersion> property as discussed above.

    If new NuGet packages are referenced transitively, e.g., by consuming Newtonsoft.Json 10.0.0 from a .NET Core 1.0 application (see GitHub issue), some extra DLL files might be added to the project's output. These are newer versions of DLL files that are part of the runtime, but override the versions from the runtime.

    If you really want to make sure that you don't use any FTS versions, you'd need to explicitly reference all these packages in your csproj file so NuGet will downgrade the version of the packages used (and emit package downgrade warnings).

    The problem here is that there hasn't been a case where an issue has not been fixed in 1.0 and 1.1 packages. If this will be an issue in the future when 1.0 and 2.0 are supported, but 1.1 no longer, we will have to see how this will be handled case by case (though there certainly be pressure/requests from the community to release updated 1.1 versions as well even if not covered by Microsoft's support).

    If you use a 2.0 or higher version, those implementation packages will be trimmed out of the dependency graph of your application and no longer be considered when deploying. This happens as part of the conflict resolution logic that knows that the new flat package contains the same DLL files as the individual packages.

    1. Once I know which runtime version is required on the end-users machine, how can I determine (programmatically) if that version of the runtime (or a newer, backwards compatible one) is available?
    1. Scan the shared\Microsoft.NETCore.App subfolders next to dotnet.exe and implement the same logic used by the host.
    2. PInvoke into the native code of latest hostfxr.dll in host\fxr next to dotnet.exe. But this is fairly complicated to do.