You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We've designed .NET Standard & our tooling so that projects targeting .NET Framework 4.6.1 can consume NuGet packages & projects targeting .NET Standard 2.0 or earlier. Unfortunately, we've seen a few issues around that scenario. The purpose of this document is to summarize the issues, outline our plan on addressing them, and providing workarounds you can deploy with today's state of our tooling.
Symptoms and root cause
The primary symptom is that applications crash with a FileLoadException or a FileNotFoundException. Another symptom is warnings at build time regarding assembly versions. This is due to one or both of the following issues:
Missing binding redirects
Missing binaries that come from indirect NuGet packages
Missing binding redirects
.NET Standard 1.x was based around contracts. Many of these contracts shipped with .NET Framework 4.5 and later. However, different versions of .NET Framework picked up different versions of these contracts, as by-design of contract versioning. As a side effect of marking .NET Framework 4.6.1 as implementing .NET Standard 2.0, some projects will now start picking up binaries built for .NET Standard 1.5 and 1.6 (as opposed to previously where .NET Framework 4.6.1 was considered as implementing .NET Standard 1.4). This results in mismatches of the assembly versions between what was shipped in .NET Framework and what was part of .NET Standard 1.5/1.6.
This can be addressed by binding redirects. As writing them by hand sucks, we added an Automatic Binding Redirect Generation feature in .NET Framework 4.5.1. This feature is opt-in. Unfortunately, it's not enabled based on target framework, but by which target framework was selected when the project was created (as the feature is turned on via an MSBuild property that is conditionally emitted by the template). In practice, this means it's mostly off you often upgrade existing projects, rather than creating new ones.
Missing binaries
There are two primary flavors of NuGet: packages.config and PackageReference.
With packages.config, each project has a config file with a flattened graph of all the NuGet dependencies. The project file in turn has direct links to all the assets. The assets are selected at install time. None of this includes indirect NuGet references coming from referenced projects.
With PackageReference each project contains MSBuild PackageReference items. The project file contains no references to any assets as the assets are selected at build time. Package restore will compute the graph of all packages, including indirect NuGet references coming from referenced projects.
The default for .NET Framework projects is packages.config. This ensures more compatibility because PackageReference doesn't support all the features that packages.config did, for example, PowerShell install scripts and content.
The only supported mode for SDK-style projects (.NET Core/.NET Standard) is PackageReference. This means that a .NET Framework project referencing a .NET Standard project ends up crossing the streams between two different NuGet models. When the .NET Standard project references NuGet packages that the .NET Framework project doesn't reference, the application ends up missing all binaries coming from those packages.
Why has this worked before? Because with packages.config, all dependencies are copied to each project's output folder. MSBuild copies them up from there. With PackageReference, we don't copy the binaries because it relies on the consuming project to see its dependencies and extract the proper asset itself. This allows the consuming project to pick up the right assets for packages that use bait & switch (which many of the .NET packages must do).
Plan
The plan is to address these issues moving forward as follows:
Converge on PackageReference for all project types, including .NET Framework. The short-term plan for (1) is to start blocking project-to-project references in Visual Studio 15.4 that will end up crossing the streams between packages.config and PackageReference. This block is UI only; you can still edit the reference by editing the project by hand. The error message will instruct you to switch the .NET Framework project to PackageReference if you want to reference a .NET Standard project. Referencing .NET Standard binaries or NuGet packages will not require this, it's only about project-to-project references. In later releases, we plan on providing a converter. The challenge is that packages.config has features we can't offer for PackagReference across the board, in particular PowerShell install scripts and content. We'll need good guidance and mitigations, if applicable.
Ensure binding redirects are on by default. Short term, this means we need to fix our target files to make sure we turn on automatic binding redirect generation. However, binding redirects don't work well in all scenarios, when there is no application project (like unit tests or add-ins). We need to work on a plan to bring the regular “higher wins” binding policy without binding redirects. This needs a proposal and proper vetting, but it seems we've reached the point where this is necessary.
Web applications and web sites don't support automatic binding redirect generation. In order to resolve binding conflicts, you need to double click the warning in the error list and Visual Studio will add them to your web.config file.
In web application projects, you should enable PackageReference like mentioned above. In web sites, you cannot use PackageReference as there is no project file. In that case, you need to install all NuGet packages into your web site that any of the direct or indirect project references depend on.
Unit tests projects
By default, binding redirects aren't added to class library projects. This is problematic for unit testing projects as they are essentially like apps. So in addition to what's outlined in automatic binding redirects you also need to specify GenerateBindingRedirectsOutputType:
Summary
We've designed .NET Standard & our tooling so that projects targeting .NET Framework 4.6.1 can consume NuGet packages & projects targeting .NET Standard 2.0 or earlier. Unfortunately, we've seen a few issues around that scenario. The purpose of this document is to summarize the issues, outline our plan on addressing them, and providing workarounds you can deploy with today's state of our tooling.
Symptoms and root cause
The primary symptom is that applications crash with a
FileLoadException
or aFileNotFoundException
. Another symptom is warnings at build time regarding assembly versions. This is due to one or both of the following issues:Missing binding redirects
.NET Standard 1.x was based around contracts. Many of these contracts shipped with .NET Framework 4.5 and later. However, different versions of .NET Framework picked up different versions of these contracts, as by-design of contract versioning. As a side effect of marking .NET Framework 4.6.1 as implementing .NET Standard 2.0, some projects will now start picking up binaries built for .NET Standard 1.5 and 1.6 (as opposed to previously where .NET Framework 4.6.1 was considered as implementing .NET Standard 1.4). This results in mismatches of the assembly versions between what was shipped in .NET Framework and what was part of .NET Standard 1.5/1.6.
This can be addressed by binding redirects. As writing them by hand sucks, we added an Automatic Binding Redirect Generation feature in .NET Framework 4.5.1. This feature is opt-in. Unfortunately, it's not enabled based on target framework, but by which target framework was selected when the project was created (as the feature is turned on via an MSBuild property that is conditionally emitted by the template). In practice, this means it's mostly off you often upgrade existing projects, rather than creating new ones.
Missing binaries
There are two primary flavors of NuGet:
packages.config
andPackageReference
.With
packages.config
, each project has a config file with a flattened graph of all the NuGet dependencies. The project file in turn has direct links to all the assets. The assets are selected at install time. None of this includes indirect NuGet references coming from referenced projects.With
PackageReference
each project contains MSBuildPackageReference
items. The project file contains no references to any assets as the assets are selected at build time. Package restore will compute the graph of all packages, including indirect NuGet references coming from referenced projects.The default for .NET Framework projects is
packages.config
. This ensures more compatibility becausePackageReference
doesn't support all the features thatpackages.config
did, for example, PowerShell install scripts and content.The only supported mode for SDK-style projects (.NET Core/.NET Standard) is
PackageReference
. This means that a .NET Framework project referencing a .NET Standard project ends up crossing the streams between two different NuGet models. When the .NET Standard project references NuGet packages that the .NET Framework project doesn't reference, the application ends up missing all binaries coming from those packages.Why has this worked before? Because with
packages.config
, all dependencies are copied to each project's output folder. MSBuild copies them up from there. WithPackageReference
, we don't copy the binaries because it relies on the consuming project to see its dependencies and extract the proper asset itself. This allows the consuming project to pick up the right assets for packages that use bait & switch (which many of the .NET packages must do).Plan
The plan is to address these issues moving forward as follows:
Converge on
PackageReference
for all project types, including .NET Framework. The short-term plan for (1) is to start blocking project-to-project references in Visual Studio 15.4 that will end up crossing the streams betweenpackages.config
andPackageReference
. This block is UI only; you can still edit the reference by editing the project by hand. The error message will instruct you to switch the .NET Framework project toPackageReference
if you want to reference a .NET Standard project. Referencing .NET Standard binaries or NuGet packages will not require this, it's only about project-to-project references. In later releases, we plan on providing a converter. The challenge is thatpackages.config
has features we can't offer forPackagReference
across the board, in particular PowerShell install scripts and content. We'll need good guidance and mitigations, if applicable.Ensure binding redirects are on by default. Short term, this means we need to fix our target files to make sure we turn on automatic binding redirect generation. However, binding redirects don't work well in all scenarios, when there is no application project (like unit tests or add-ins). We need to work on a plan to bring the regular “higher wins” binding policy without binding redirects. This needs a proposal and proper vetting, but it seems we've reached the point where this is necessary.
Workarounds
Regular .NET Framework projects
packages.config
but usesPackageReference
for NuGet packagespackages.config
, simply add<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
to your project filepackages.config
, convert the contents to packages references in the project file. The syntax is like this:<PackageReference Include="package-id" Version="package-version" />
ASP.NET web applications and web sites
web.config
file.PackageReference
like mentioned above. In web sites, you cannot usePackageReference
as there is no project file. In that case, you need to install all NuGet packages into your web site that any of the direct or indirect project references depend on.Unit tests projects
By default, binding redirects aren't added to class library projects. This is problematic for unit testing projects as they are essentially like apps. So in addition to what's outlined in automatic binding redirects you also need to specify
GenerateBindingRedirectsOutputType
:Discussion
For a discussion, please go to dotnet/standard#481.
The text was updated successfully, but these errors were encountered: