Determine which .NET Core runtime is needed - asp.net-core

I found some interesting articles about the difficulties of .NET Core SDK/runtime/tooling versioning, for example:
.NET Core Versioning
.NET Core versioning
Supporting both LTS and Current releases for ASP.NET Core
However I still don't really know how to deal with all of this in practice:
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?
Does the runtime version need to match exactly, or can the runtime installed on the end-users machine be newer than the required version?
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:
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?

First, let's look at what happens when a portable .NET Core application is run via dotnet yourapp.dll:
The muxer executable (dotnet.exeon windows) loads a version of the host framework resolver (hostfxr) from a folder beneath host\fxr\ next to dotnet.exe.
The host framework resolver looks at yourapp.runtimeconfig.json (or a different file if configured when using dotnet exec) to find out which framework you are targeting. This extracts a framework name (Microsoft.NETCore.App) and a version (e.g. 1.1.0).
It then looks inside the shared\Microsoft.NETCore.App folder (based on the framework name) for available versions.
Based on the available versions and the framework version from yourapp.runtimeconfig.json it determines which version to use. Or it may decide to error out and complain that the required version is not available.
👆 this is the hard and sometimes confusing part.
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 is also no version roll-forward across minor versions. So an app with a runtime config 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 no matching minor version is found - if a 1.0.5 and 1.1.2 runtime are installed, an app with runtime config of 1.0.4 will be run on the 1.0.5 runtime. If only 1.1.2 is installed, the same app will be run on 1.1.2. If only 2.0.0 is installed, the same app 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 config comes from. When you target the framework netcoreapp1.1, the tooling you use will determine:
Which NuGet package (+ version) to use so you get the compilation references to be able to compile.
Which version to write into the yourapp.runtimeconfig.json
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:
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 app cannot be run on the target system. (e.g. 1.0.5 app and only 2.0.0 runtime)
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 config is a hard minimum. For choosing the right version of newer runtimes see above.
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 app (see GitHub issue), some extra DLLs might be added to the project's output. These are newer versions of DLLs 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 app 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.
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?
Scan the shared\Microsoft.NETCore.App subfolders next to dotnet.exe and implement the same logic used by the host.
PInvoke into the native code of latest hostfxr.dll in host\fxr next to dotnet.exe. But this is fairly complicated to do.

Related

.NET confused, too many files

I have a server running tons of apps like IIS, VS, VS Code etc. Can someone tell me what these folders are:
%programfiles%\dotnet\shared\Microsoft.NETCore.App\
Microsoft.AspNetCore.App
Microsoft.WindowsDesktop.App
I have an old version of .NET Core but .NET Core uninstall tool cannot find it, it doesn't even show up in add or remove program.
Can I download the latest binaries of the SDK and just swap the directories?
shared/Microsoft.NETCore.App/<runtime version> This framework contains the .NET runtime and supporting managed libraries.
shared/Microsoft.AspNetCore.{App,All}/<aspnetcore version> contains the ASP.NET Core libraries. The libraries under Microsoft.AspNetCore.App are developed and supported as part of the .NET project. The libraries under Microsoft.AspNetCore.All are a superset that also contains third-party libraries.
Microsoft.WindowsDesktop.App.Ref describes the API of x.y version of Windows Desktop applications. These files are used when compiling for that target. This isn't provided on non-Windows platforms.
For more packaging details ,you can refer to this document.
If you can't uninstall the old version of .net core, you can download the version you want and specify to use this version in your project. You can take a look at this case.

How to enforce specific package version on sub-dependency for netstandard library

I am building a netstandard2.0, with a series of sub-dependencies, including Azure.Data.Tables. I have found that Azure.Data.Tables on >=12.3.0 is not compatible with 2.2 projects, but version 12.0.0 is (or at least it doesn't crash my .Net Core 2.2 application).
Based on these premises the resolution was simple: put version 12.0.0 in the netstandard library dependencies. There's only one problem, that when I install this library using Visual Studio into my Net Core 2.2 project I get that visual studio decides to install 12.3.0.
I have checked documentation around enforcing certain versions using the allowed wildcards(https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges-and-wildcards), such as:
<PackageReference Include="Azure.Data.Tables" Version="[12.0.0]" />
Which produces this package description just before pressing "install".
But it doens't seem to work, Visual Studio simply carries on with the 12.3.0 installation.
It turns out Visual Studio was just using the nuget cache for my locally generated packages. If I either changed the version of the packaged package then the sub-dependencies would change. As well, another way of getting the precise package would be to clear nuget's cache locally: nuget locals all -clear.

Understanding ASP.NET Core Patch Version Roll Forward

I'm trying to understand how the roll-forward mechanism with Microsoft.AspNetCore.App is supposed to work along with other nuget packages.
My understanding so far is that any time I publish my app, .netcore will automatically publish the app on the latest patched version that I have installed. This doesn't sound like a good practice to me. I think the application should be deployed on the .net version that it was developed on.
I recently ran into a problem where my build server has a newer SDK installed than I have on my development machine, so it tried to publish the app with a newer .netcore version.
This caused the build to break with the following error:
error NU1605: Detected package downgrade: Microsoft.EntityFrameworkCore.Design from 2.1.8 to 2.1.4. Reference the package directly from the project to select a different version.
I have explicitly specified that I want version 2.1.4 of Microsoft.EntityFrameworkCore.Design because that was the latest version that was available when I originally developed the application.
I feel like I'm missing something here. Am I supposed to make sure that I always
have the same SDK installed locally as the build server and that all of my apps are updated to the latest SDK every time I need to make a change? What if a co-worker wants to write a .netcore app targeting a newer version of the framework so they request that a new SDK be installed on the build server? Surely I shouldn't have to also update my app to the latest patch version.
You experienced the problem because when you reference the versionless Microsoft.AspNetCore.App you shouldn't reference packages which already are pulled in by the metapackage.
Because the metapackage already contains Microsoft.EntityFrameworkCore.Design and you specified a hard version you got a version conflict.
This usually happens when you have one project with the metapackage in another (for example test project) have a specified version. What happens then is that when building it will determine the highest version and use that but when it gets to the test project it will need to downgrade (because of the specified version) and will throw a build error. The recommended way until 3.0 is to add <Project Sdk="Microsoft.NET.Sdk.Web"> to the test project and also use the metapackage.
As far as I know it works like this: When you reference Microsoft.AspNetCore.App it will take the lowest possible version it can. So if for example your targetframework is netcoreapp2.1 then it will take 2.1.1. When it builds it will look what the highest possible version is of the shared framework you have installed for and use that. If you have the following runtimes installed: 2.2.0, 2.1.5, 2.1.12 it will roll forward to 2.1.12. There are some additional roll forward policies having to do with the minor (if its a preview version or not ect.) but those only happen when it can't find a runtime corresponding to the major and minor version.
See also: https://github.com/aspnet/AspNetCore/issues/3292 and https://github.com/aspnet/AspNetCore.Docs/issues/6430

How do I prevent installing nuget packages that aren't 100% .Net Core 2.0 compatible?

I understand that .Net Core 2 has a compatibility shim that allows it use Nuget packages that don't specifically target .Net Core/Standard 2. This gives it access to 70% of Nuget. Great - nice feature.
How do I prevent installing nuget packages that aren't fully compatible with .Net Core 2/.Net Standard 2? Or warn me at point of installing that it is being used with the shim?
I created a new .Net Core 2.0 project installed EF 6.1.3 (which I knew didn't work) and nothing prevented me or warned that it didn't target .Net Standard <=2 at the point of install.
I am happy to "run with scissors" but I kind of feel I should be getting a warning before I install MVC5 and EF 6.1.3 into a .Net Core 2 application. I would really like to prevent junior devs from installing unsupported packages etc.
I guess further to Matt Ward answer - my main point is - can it be detected that something is actually 100% compatible at install or are we always just in the situation where we need to make determination ourselves that package works "well enough". I hoped that there was a technical mechanism that detected missing coverage API coverage and could tell us that the nuget package may not operate as it did before. So I guess MS say 70% compatibility - I want to fail if I try to install the 30%
Installing Entity Framework 6.1.3 into a .NET Core 2.0 project there is a NU1701 warning in the Errors window about Entity Framework 6.1.3 being restored using .NET Framework 4.6.1 and that it may not be fully compatible.
You could turn the NU1701 warning into an error in the project so you could not install any NuGet package that does not explicitly support .NET Core 2.0. This can be done by adding the WarningAsErrors property to the project.
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<WarningsAsErrors>NU1701</WarningsAsErrors>
</PropertyGroup>
Then if you try to install Entity Framework 6.1.3 the restore will fail, the changes will be rolled back, and the NuGet package will not be installed.
You can also set the DisableImplicitAssetTargetFallback property to true which will prevent .NET 4.6.1 being added to the AssetTargetFallback property which is used when checking NuGet package compatibility with .NET Core 2.0 projects.
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<DisableImplicitAssetTargetFallback>true</DisableImplicitAssetTargetFallback>
</PropertyGroup>
If you wan to be 100% sure, run the package against .NET Portability Analyzer and .NET Standard 2.0 profile.
It won't tell you if the API will be called or not (and is in no way an automatic process), just if the Assembly contains any API which is not .NET Standard 2.0 compatible.
However, you can also only run your application against the Analyzer, as the .NET Portability Analyzer should be able to follow any references made from the application and check these too.
Update
You can als build this into your build server pipeline, to get a more automatic guarantees.
The .NET Portability Analyzer Docs.
Visual Studio is not required for this, just download it from https://github.com/Microsoft/dotnet-apiport/releases and run
From the docs:
Type the following command to analyze the current directory: \...\ApiPort.exe analyze -f .
To analyze a specific list of .dll files, type the following command: \...\ApiPort.exe analyze -f first.dll -f second.dll -f third.dll
Old Answer (may be useful in conjcution with Matt's answer)
Untested, but give it a try:
<!-- old dotnet tooling/.NET Core 1.x -->
<PackageTargetFallback>netstandard2.0;portable-net45+win8</PackageTargetFallback>
<!-- new dotnet tooling/.NET Core 2.0 -->
<AssetTargetFallback>netstandard2.0;portable-net45+win8</AssetTargetFallback>
Typically you want to have it like
<!-- old dotnet tooling/.NET Core 1.x -->
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
<!-- new dotnet tooling/.NET Core 2.0 -->
<AssetTargetFallback>$(AssetTargetFallback);dotnet5.6;portable-net45+win8</AssetTargetFallback>
Where $(PackageTargetFallback) will tell MSBuild to keep the old values and append the values after that to it. But since $(PackageTargetFallback) probably (can't look/dig deeper in right now) have the .NET Framework moniker there, you'll override it with your own values.
Additionally
Given that PackageTargetFallback is now deprecated, one should use AssetTargetFallback instead.
As far as I know .Net portability analyzer tool can not 100% determine the platform that does not support installation, such as system.runtime.Loader, after tool analysis, 100% supports the framework platform, but it does not
Screenshot of analysis results:analyse System.Runtime.Loader

Check if package is compatible with .net core

I started programming with .NET Core and ASP.NET Core and after reading many Docs/Tutorials i still am not sure of how to realize if a Nuget-(Meta-)Package is comptabile/usable within my .NET-Core-App.
How to check if a NuGet-Package is compatible with .NET Core.
E.g. the often recited Newtonsoft JSON. Is it compatible/usable - and how to see this?
Is there a list of all the available .NET Core packages?
(Like here it lists a few
key NuGet packages for .NET Core
). But since they say those are "key" packages i would assume there are more. But which?
The best way to understand compatibility is table here
In this table you can check what API version support NuGet package. If it is standard 1.0+ - it works with .Net Core
For checking (supported API version) dependencies on your package, you can check page of package on nuget.org or in your package manager
Here is an example for Rider
maybe that will be helpful - lots of popular packages https://github.com/thangchung/awesome-dotnet-core
I also don't see nuget.org mentioned anywhere so:
https://nuget.org
General rule of thumb for me is:
if package has a dependency on net standard or .net core, it will run with .net core(mind the versions also)
https://packagesearch.azurewebsites.net
Go to site and search for package to find its compatibility
Well, in fact you don't have to worry, the NET core application will indicate you if the package is compatible or not when you will run a dotnet restore command in your project.
Let say that you have the famous CSVHelper package registered in your csproj file :
<PackageReference Include="CsvHelper" Version="0.12.0"/>
Then, when you'll run any dotnet command such as build or run, you'll have the following input in the console in case of, here, cross-compatibility:
YourProjet/aspnetapp.csproj : warning NU1701: Package 'CsvHelper 0.12.0' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' instead of the project target framework '.NETCoreApp,Version=v2.2'. This package may not be fully compatible with your project.
Basically, it mean that NET Core application can use ASP.NET NuGet package, and so far, I've never cross on a non-compatible package. Even if version is incorrect, the build will get the most recent matching version of the NuGet package.
Hoped it answer your question.