Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding redirect when importing AngleSharp in .Net 4.7.2 somehow tries to pull in NetStandard 2 as a dll

I have a legacy app running on an old Windows 2008 R2 server. The app has gone through many .Net versions, and is currently on 4.7.2. It has been stable as a rock for years, and while there are migration plans to move away from the old physical server, it has to live for a while longer.

A new feature sees the need for some HTML scraping. I added the NuGet package for AngleSharp v0.12.0, unit tested and all was well on my Win10 dev machine. The new class that used AngleSharp simply worked.

So I deployed to the prod server, including the changes to app.config;

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

These are the only changes I could identify from the older version.

When I run the app in production, it crashes.

System.IO.FileLoadException: Could not load file or assembly 'System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

After doing a bit of searching online, I saw someone point out a similar problem (.Net Core however, but still similar), and they pointed out that trying to call Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); might make a difference. Reference: https://github.com/DiscUtils/DiscUtils/issues/77

It didn't for me, but now I got more funky messages:

System.IO.FileNotFoundException: Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.

How did netstandard get into this? I don't know. I also see this in the binding log that I get thanks to Splats exception logging:

5496 === Pre-bind state information ===
5497 LOG: DisplayName = netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
5498  (Fully-specified)
5499 LOG: Appbase = file:///E:/dcollect/Ingest/
5500 LOG: Initial PrivatePath = NULL
5501 Calling assembly : System.Text.Encoding.CodePages, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.
5502 ===
5503 LOG: This bind starts in default load context.
5504 LOG: Using application configuration file: E:\dcollect\Ingest\Ingest.exe.Config
5505 LOG: Using host configuration file:
5506 LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
5507 LOG: Post-policy reference: netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
5508 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/netstandard.DLL.
5509 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/netstandard/netstandard.DLL.
5510 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/netstandard.EXE.
5511 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/netstandard/netstandard.EXE.

I have no known (to me) references to Netstandard in my project, all the projects are .Net Framework 4.5, 4.6.x or 4.7.x projects.

I also came across this one that seems like it might be related: https://github.com/dotnet/announcements/issues/31

I tried converting my project to a PackageReference project, but that also did not change anything.

For completeness, here is the binding log for my initial attempt at using AngleSharp.

5576 [20:24:01.522] <ERROR> EventParser: Exception caught: System.IO.FileLoadException: Could not load file or assembly 'System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
5577 File name: 'System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
5578    at AngleSharp.Browser.EncodingMetaHandler..ctor()
5579    at AngleSharp.Configuration..ctor(IEnumerable`1 services)
5580    at AngleSharp.Configuration.get_Default()
5581    at Teller.Charts.DataCollection.EventParser.ParseHTML(String html)
5582 === Pre-bind state information ===
5583 LOG: DisplayName = System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
5584  (Fully-specified)
5585 LOG: Appbase = file:///E:/dcollect/Ingest/
5586 LOG: Initial PrivatePath = NULL
5587 Calling assembly : AngleSharp, Version=0.11.0.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea.
5588 ===
5589 LOG: This bind starts in default load context.
5590 LOG: Using application configuration file: E:\dcollect\Ingest\Ingest.exe.Config
5591 LOG: Using host configuration file:
5592 LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
5593 LOG: Post-policy reference: System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
5594 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/System.Text.Encoding.CodePages.DLL.
5595 WRN: Comparing the assembly name resulted in the mismatch: Minor Version
5596 ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

I am sure there must be some simple redirect magic I am missing here. Is it possible to convince my project to load this for Net Framework v4.7.2 and just work?

Thanks for any suggestions!

EDIT MAY 14th: The point of peak confusion for me is all the different version numbers involved.

The AngleSharp 0.12.0 NuGet package depends on System.Text.Encoding.CodePages >= 4.5.0.

So it automatically installed v4.5.0 even if v4.5.1 exists because lowest dependency behavior.

If I right-click the actual DLL that gets put into my bin\Debug folder, choose Properties and then Version, it reports File and product version 4.6.26515.

But in my app.config, the binding redirect I get refers to 4.1.1.0.

    <dependentAssembly>
      <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
    </dependentAssembly>

(I tried replacing both instances of 4.1.1.0 with 4.6.26515.0 just for fun but that changed nothing)

And then, as stated above, the log claims:

29014 [22:42:44.735] <ERROR> EventInfoCreator: Failed to parse: System.IO.FileLoadException: Could not load file or assembly 'System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
29015 File name: 'System.Text.Encoding.CodePages, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
29016    at AngleSharp.Browser.EncodingMetaHandler..ctor()
29017    at AngleSharp.Configuration..ctor(IEnumerable`1 services)
29018    at AngleSharp.BrowsingContext.New(IConfiguration configuration)
29019    at Teller.Charts.DataCollection.EventParser.ParseHTML(String html)
29020    at Teller.Charts.DataCollection.EventParser.ParseFile(String filePath)
29021    at Teller.Charts.DataCollection.EventInfoCreator.CreateEventInfoProps(String html, String storageDirectory)

So there is obviously some version mismatch here, but I frankly have no idea which of these version numbers are important.

29034 LOG: Attempting download of new URL file:///E:/dcollect/Ingest/System.Text.Encoding.CodePages.DLL.
29035 WRN: Comparing the assembly name resulted in the mismatch: Minor Version

This seems to indicate that it is unhappy with the minor version, but... which one?

4.0.2.0 that AngleSharp seems to ask for? 4.1.1.0 that the app.config refers to? 4.6.26515.0 that is the version the dll actually claims to be?

like image 768
Rune Jacobsen Avatar asked May 12 '19 18:05

Rune Jacobsen


2 Answers

My team has run into various binding redirect problems over this past year, with System.Net.Http, System.IO, and System.Runtime being the major culprits for adding incorrect redirects. It's caused us a lot of grief, but I have found a set of steps that has fixed things for us.

  1. Delete all binding redirects from all projects
  2. Open package manager console
  3. Run Get-Project –All | Add-BindingRedirect

That command will add the required binding redirects for your projects, but it won't delete unused ones which is why I've added the first step for deleting all redirects beforehand.

It seems like a bit of a sledgehammer for a couple of packages, but I was amazed at how much junk it removed from our, admittedly old, solution, whilst fixing the problems we were having.

like image 96
John H Avatar answered Nov 16 '22 07:11

John H


This problem was solved. I can't entirely explain the solution, but I just wanted to put this here in case it could help anyone else in a similarly bizarre situation.

As mentioned, the runtime environment is a Win2008R2 server. Compiling the system for .Net 4.7.2 seemed to work fine, all the way until AngleSharp was invoked (and it in turn pulled in the CodePages reference).

Now, after talking to Florian who runs the AngleSharp project, he was worried something evil might be lurking in the GAC. I searched, found nothing - but noticed that .Net 4.7.2 was indeed not installed on the server! And I soon discovered why - nothing beyond 4.6.2(?) is actually supported on this ancient platform.

That does in no way explain why my own code - compiled for 4.7.2 - ran without a hitch, but bombed as soon as it called an external class library. It probably does explain it to someone who knows more about .NET runtimes than I do, however.

So I set the projects to be version 4.6.2, compiled, and now everything runs buttery smooth. No problems loading any dependencies or anything.

In retrospect this should probably have been obvious, but I'm sticking to my story about being taken for a ride because of my executable and at least one class library being compiled for 4.7.2 and still running.

Mind your versions and platform support, folks. :|

like image 24
Rune Jacobsen Avatar answered Nov 16 '22 07:11

Rune Jacobsen