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
Just wanting to put this information somewhere to consolidate some research around the issue and what I found to be a sane solution as of 2024-11-14 in hopes that it helps other people in the same situation trying to develop netstandard2.0 packages to be consumed by both .NET Framework 4 and .NET SDK applications, with the newer tooling.
I realize that this problem is old and .NET Framework 4.6.1 is unsupported, but the lack of consolidated documentation about this subtle behaviour has cost me and many others a lot of headache, so I'm hoping to fill that gap with this issue.
Environment
VS 2022 Professional (17.10.5)
VS 2019 Build Tools (required to work with .NET Framework 4.6.1)
Get installation codenum with Get-ItemPropertyValue -LiteralPath 'HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release
.NET Framework 4.6.1 web project with a netstandard2.0 dependency
Problem
With the introduction of netstandard2.0, some workarounds were introduced to assembly resolution process of MSBuild / Visual Studio which can sometimes not play nicely with dependencies that depend on the System.Net.Http package versions currently on NuGet, which are now deprecated, because netstandard2.0 ships with version 4.2.0.0, and .NET Framework 4.6.1 is declared compatible retroactively. So, some older .NET Framework installs may not have this System.Net.Http 4.2.0.0 version available, causing failures at runtime due to missing API surface.
Assembly binding redirect management seems to be the crux of this issue, where the version range generated can sometimes be incorrect (at least for me and a few of our devs, it was).
Solution
TL;DR
Ensure your Windows hosting environment is running NET Framework 4.8+
Ensure project(s) in your solution depend on System.Net.Http without
specifying a version:
in .csproj <Reference Include="System.Net.Http" /> and ensure no other
previous references to System.Net.Http are present
Ensure no binding redirect is present in Web.config / App.config for System.Net.Http.
This way, all projects rely on the netstandard2.0 standardization of System.Net.Http, which is backwards compatible and should not break
anything.
After fixing the references to System.Net.Http and reloading solution, Update-Package -Reinstall and/or Add-BindingRedirect in VS Package Manager
Console should removeSystem.Net.Http binding redirect entirely.
I noticed that to fix the issue, a full reload of a solution in VS was
required in order for Add-BindingRedirect to correctly remove assembly
redirect for System.Net.Http in our web projects after changes to references
were made.
Issue #29314 has the most relevant information out of any issue discussion that I have discovered and eventually led me to this solution.
After having a few previous run-ins with various System.Net.Http assembly versions not being found at runtime due to incorrect binding redirects, I found myself facing a new issue once updating a dependency of a web project to target netstandard2.0. The resolved dependency in References view of solution explorer switched from NuGet package (pkg version 4.3.4 but really, assembly version 4.1.1.3) to assembly version 4.2.0.0 which resolved to a path under VIsual Studio 2022 installation.
I assume that the default behaviour to not simply resolve 4.2.0.0 regardless of having a netstandard2.0 dependency was to not break deployments of 4.6.1 applications to Windows environments that did not have up to date .NET Framework installations with netstandard2.0 backwards compatibility patches to 4.6.1 runtime, as System.Net.Http 4.2.0.0 is part of netstandard2.0 which was released much later (for context, .NET Framework 4.6.1 supporting .NET Standard 2.0).
So, this solution assumes you are deploying to an environment with .NET Framework >= 4.8 (possibly 4.7.1 works? hard to tell since there were some fixes between 4.7.1 and 4.7.2 related to some missing stabilized netstandard2.0 API surface). 4.8 is available in patches to now very old Windows releases (e.g. Server 2012) so for web hosting, this should not be an issue as you can keep the same targetFramework and still run latest install in hosting environment (e.g. I deploy 4.6.1 target for web projects but hosting environments (Windows Server, Azure App Service) have 4.8 installed).
In order to verify that binding redirects were not required, I used fuslogvw command by running VS as administrator and opening Tools > Command Line > Developer Powershell, running fuslogvw and changing Settings > Log all binds to disk.
Debugging the project with (left) and without (right) the binding redirect to 4.2.0.0, which was added manually for this test since I have binding redirects generating correctly now, I captured the following assembly loading diagnostics and added some added annotations and in-lined investigation:
Even though the redirect was working according to fuslogvw, the same assembly version 4.0.0.0 is being loaded, with the only obvious difference being that not having the redirect causes the assembly to be loaded from GAC (Global Assembly Cache). I attribute this behavior to work of the NET Framework 4.6.1 and netstandard2.0 retroactive compat layer magic.
To confirm that the two successfully bound DLL files were in fact the same assembly, I compared them all in powershell, and additionally, to the one copied to bin/ folder on build, and an unrelated assembly to make sure the .Equals was working as expected:
All three System.Net.Http.DLLs are the exact same assembly still reporting version 4.0.0.0.
So, is it using 4.2.0.0 but reporting as 4.0.0.0 in all cases?
Conclusion
The results are that System.Net.Http continues to load properly and work correctly, given that:
the web project at framework 4.6.1, which reports reference to 4.2.0.0 in VS due to a netstandard2.0 package being referenced
an additional class lib project at framework 4.6.1, which the web project depends on, reports a reference to version 4.0.0.0 in VS (no netstandard2.0 dependency in this project)
no binding redirect required for System.Net.Http
Appendix
fuslogvw output (with redactions)
With binding redirect to 4.2.0.0 added manually:
*** Assembly Binder Log Entry (2024-11-13 @ 8:09:25 PM) ***
The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable C:\Program Files\IIS Express\iisexpress.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: DisplayName = System.Net.Http
(Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: System.Net.Http | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/Users/MyUsername/projects/web/API/app/API/RedactedProjectName/
LOG: Initial PrivatePath = C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4
LOG: AppName = 9b699876
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\web.config
LOG: Using host configuration file: C:\Users\MyUsername\Documents\IISExpress\config\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/vs/248979d4/9b699876/System.Net.Http.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/vs/248979d4/9b699876/System.Net.Http/System.Net.Http.DLL.
LOG: Attempting download of new URL file:///C:/Users/MyUsername/projects/web/API/app/API/RedactedProjectName/bin/System.Net.Http.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\bin\System.Net.Http.dll
LOG: Entering download cache setup phase.
LOG: Assembly Name is: System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: A partially-specified assembly bind succeeded from the application directory. Need to re-apply policy.
LOG: Using application configuration file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\web.config
LOG: Using host configuration file: C:\Users\MyUsername\Documents\IISExpress\config\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Redirect found in application configuration file: 4.2.0.0 redirected to 4.2.0.0.
LOG: Post-policy reference: System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: Binding succeeds. Returns assembly from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4\9b699876\assembly\dl3\43dbadef\50376cf4_05e2da01\System.Net.Http.dll.
LOG: Assembly is loaded in default load context.
...
PS C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4\9b699876\assembly\dl3\43dbadef\50376cf4_05e2da01> ([system.reflection.assembly]::loadfile((get-item "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4\9b699876\assembly\dl3\43dbadef\50376cf4_05e2da01\System.Net.Http.dll").FullName)).FullName
System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
No binding redirect for System.Net.Http:
*** Assembly Binder Log Entry (2024-11-13 @ 8:11:41 PM) ***
The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable C:\Program Files\IIS Express\iisexpress.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: DisplayName = System.Net.Http
(Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: System.Net.Http | Domain ID: 2
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///C:/Users/MyUsername/projects/web/API/app/API/RedactedProjectName/
LOG: Initial PrivatePath = C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\bin
LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4
LOG: Cache Base = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4
LOG: AppName = 9b699876
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\web.config
LOG: Using host configuration file: C:\Users\MyUsername\Documents\IISExpress\config\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/vs/248979d4/9b699876/System.Net.Http.DLL.
LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/vs/248979d4/9b699876/System.Net.Http/System.Net.Http.DLL.
LOG: Attempting download of new URL file:///C:/Users/MyUsername/projects/web/API/app/API/RedactedProjectName/bin/System.Net.Http.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\bin\System.Net.Http.dll
LOG: Entering download cache setup phase.
LOG: Assembly Name is: System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: A partially-specified assembly bind succeeded from the application directory. Need to re-apply policy.
LOG: Using application configuration file: C:\Users\MyUsername\projects\web\API\app\API\RedactedProjectName\web.config
LOG: Using host configuration file: C:\Users\MyUsername\Documents\IISExpress\config\aspnet.config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Version redirect found in framework config: 4.2.0.0 redirected to 4.0.0.0.
LOG: Post-policy reference: System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: Binding succeeds. Returns assembly from C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Net.Http\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Net.Http.dll.
LOG: Assembly is loaded in default load context.
...
somehow its the same assembly version???
PS C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4\9b699876\assembly\dl3\43dbadef\50376cf4_05e2da01> ([system.reflection.assembly]::loadfile((get-item "C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Net.Http\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Net.Http.dll").FullName)).FullName
System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
PS C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\248979d4\9b699876\assembly\dl3\43dbadef\50376cf4_05e2da01>
The text was updated successfully, but these errors were encountered:
Just wanting to put this information somewhere to consolidate some research around the issue and what I found to be a sane solution as of
2024-11-14
in hopes that it helps other people in the same situation trying to developnetstandard2.0
packages to be consumed by both .NET Framework 4 and .NET SDK applications, with the newer tooling.I realize that this problem is old and .NET Framework 4.6.1 is unsupported, but the lack of consolidated documentation about this subtle behaviour has cost me and many others a lot of headache, so I'm hoping to fill that gap with this issue.
Environment
533325
==4.8.1
)Get-ItemPropertyValue -LiteralPath 'HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release
netstandard2.0
dependencyProblem
With the introduction of
netstandard2.0
, some workarounds were introduced to assembly resolution process of MSBuild / Visual Studio which can sometimes not play nicely with dependencies that depend on theSystem.Net.Http
package versions currently on NuGet, which are now deprecated, becausenetstandard2.0
ships with version4.2.0.0
, and .NET Framework 4.6.1 is declared compatible retroactively. So, some older .NET Framework installs may not have this System.Net.Http4.2.0.0
version available, causing failures at runtime due to missing API surface.Assembly binding redirect management seems to be the crux of this issue, where the version range generated can sometimes be incorrect (at least for me and a few of our devs, it was).
Solution
Issue #29314 has the most relevant information out of any issue discussion that I have discovered and eventually led me to this solution.
After having a few previous run-ins with various
System.Net.Http
assembly versions not being found at runtime due to incorrect binding redirects, I found myself facing a new issue once updating a dependency of a web project to targetnetstandard2.0
. The resolved dependency inReferences
view of solution explorer switched from NuGet package (pkg version4.3.4
but really, assembly version4.1.1.3
) to assembly version4.2.0.0
which resolved to a path under VIsual Studio 2022 installation.I assume that the default behaviour to not simply resolve
4.2.0.0
regardless of having anetstandard2.0
dependency was to not break deployments of4.6.1
applications to Windows environments that did not have up to date .NET Framework installations withnetstandard2.0
backwards compatibility patches to4.6.1
runtime, asSystem.Net.Http 4.2.0.0
is part ofnetstandard2.0
which was released much later (for context, .NET Framework 4.6.1 supporting .NET Standard 2.0).So, this solution assumes you are deploying to an environment with .NET Framework
>= 4.8
(possibly4.7.1
works? hard to tell since there were some fixes between4.7.1
and4.7.2
related to some missing stabilizednetstandard2.0
API surface).4.8
is available in patches to now very old Windows releases (e.g. Server 2012) so for web hosting, this should not be an issue as you can keep the same targetFramework and still run latest install in hosting environment (e.g. I deploy4.6.1
target for web projects but hosting environments (Windows Server, Azure App Service) have4.8
installed).In order to verify that binding redirects were not required, I used
fuslogvw
command by running VS as administrator and openingTools > Command Line > Developer Powershell
, runningfuslogvw
and changingSettings > Log all binds to disk
.Debugging the project with (left) and without (right) the binding redirect to
4.2.0.0
, which was added manually for this test since I have binding redirects generating correctly now, I captured the following assembly loading diagnostics and added some added annotations and in-lined investigation:Even though the redirect was working according to
fuslogvw
, the same assembly version4.0.0.0
is being loaded, with the only obvious difference being that not having the redirect causes the assembly to be loaded from GAC (Global Assembly Cache). I attribute this behavior to work of the NET Framework4.6.1
andnetstandard2.0
retroactive compat layer magic.To confirm that the two successfully bound DLL files were in fact the same assembly, I compared them all in powershell, and additionally, to the one copied to
bin/
folder on build, and an unrelated assembly to make sure the.Equals
was working as expected:All three
System.Net.Http.DLL
s are the exact same assembly still reporting version4.0.0.0
.So, is it using
4.2.0.0
but reporting as4.0.0.0
in all cases?Conclusion
The results are that
System.Net.Http
continues to load properly and work correctly, given that:4.6.1
, which reports reference to4.2.0.0
in VS due to anetstandard2.0
package being referenced4.6.1
, which the web project depends on, reports a reference to version4.0.0.0
in VS (nonetstandard2.0
dependency in this project)System.Net.Http
Appendix
fuslogvw output (with redactions)
With binding redirect to
4.2.0.0
added manually:No binding redirect for
System.Net.Http
:The text was updated successfully, but these errors were encountered: