6

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?

Rune Jacobsen
  • 9,907
  • 11
  • 58
  • 75

2 Answers2

4

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.

John H
  • 14,422
  • 4
  • 41
  • 74
  • 1
    Thanks for your suggestion - I tried to follow the steps exactly, and it actually bailed on the project I am struggling with: ```Add-BindingRedirect : Project 'Libraries\Teller.Charts' is not found. At line:1 char:20 + Get-Project -All | Add-BindingRedirect + ~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (Libraries\Teller.Charts:String) [Add-BindingRedirect], ItemNotFoundException + FullyQualifiedErrorId : NuGetProjectNotFound,NuGet.PackageManagement.PowerShellCmdlets.AddBindingRedirectCommand``` The project is there, it builds... Confusing! – Rune Jacobsen May 12 '19 at 19:19
  • Further to that... a sibling project is found just fine if I just run Get-Project with the name of it, while this one is not found. Is there something weird going on with my project? So it would seem. – Rune Jacobsen May 12 '19 at 19:22
  • This seems to have been related to the move from packages.config to PackageReference. Moving back allowed the project to be found by Get-Project. Unfortunately, after allowing it to update app.config with Add-BindingRedirect, the problem is still there. :( – Rune Jacobsen May 12 '19 at 19:31
  • @RuneJacobsen Thanks for trying the above, and sorry it hasn't worked for you. I've not run into that problem before. One question: have you actually tried installing the `System.Text.Encoding.CodePages` package into your project? – John H May 12 '19 at 19:35
  • Yes, I tried to install it, both into the class library project where AngleSharp is used, and in the executable that uses the class library. I also tried the same with System.Runtime.CompilerServices.Unsafe which the CodePages in turn depended on, but no luck. – Rune Jacobsen May 12 '19 at 19:41
  • Just for completeness - I also tried updating CodePages and CompilerServices.Unsafe to their latest versions, rather than the ones specified by AngleSharp, but this made no difference either. – Rune Jacobsen May 12 '19 at 19:45
  • @RuneJacobsen Try deleting those 2 binding redirects. – John H May 12 '19 at 19:48
  • Just did - same error as in my original post. Thanks for trying though. :) – Rune Jacobsen May 12 '19 at 19:51
  • @RuneJacobsen No problem. What framework version is your class library targeting? – John H May 12 '19 at 19:58
  • The class library has a target framework of .NET Framework 4.7.2. – Rune Jacobsen May 12 '19 at 20:08
  • @RuneJacobsen I'm genuinely stumped, but I did see [this issue](https://github.com/AngleSharp/AngleSharp/issues/740) for AngleSharp itself. It seems other people have had this same kind of problem. I think you've tried everything suggested there, but it might be worth double checking. – John H May 12 '19 at 20:21
  • @RuneJacobsen Perhaps you could try using a version of AngleSharp that's `<` `0.10.0` to see if that helps, too. – John H May 12 '19 at 20:25
  • Got late here, will follow up on these points tomorrow, thank you very much for trying to help out! – Rune Jacobsen May 12 '19 at 21:00
  • @RuneJacobsen No problem mate. Same for me. Have a good one. :) – John H May 12 '19 at 21:01
  • Thanks! I tried installing AngleSharp 0.9.11 (after removing the newer one and its dependencies), but alas, same problem. Interestingly I get in the log `Calling assembly : AngleSharp, Version=0.11.0.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea.` - this matches up with the version actually on disk being 0.11.0.0 (both file and "product" version). – Rune Jacobsen May 13 '19 at 04:31
  • It seems no matter what NuGet package I add, the version that ends up in my build directory is 0.11.0.0. There must be a poltergeist here. – Rune Jacobsen May 13 '19 at 04:35
  • @RuneJacobsen That shouldn't be happening unless you need to consolidate package versions across your projects. Maybe do a text search of that package version to make sure nothing else is referencing it by mistake (although consolidating your packages should sort that out). You could also do a clean and full rebuild afterwards, too. – John H May 13 '19 at 17:33
  • This is legit driving me mad, mate. I cleaned the solution, physically deleted the bin\Debug directories for both the class library and the executable that uses the lib, checked that only the library uses AngleSharp in the whole solution, and that it is 0.9.9.2. I checked in the solution "packages" directory, and the AngleSharp version in there is 0.9.9.2 for sure. I even switched back from VS2019 to VS2017 in case that did something differently. So I build... and AngleSharp v0.11.0.0 (seen as v0.12.0.0 in NuGet) returns to the Debug directories. Where is it coming from!? :O – Rune Jacobsen May 13 '19 at 18:09
  • I've gone nuclear. I disabled ReSharper (just in case it did some caching), I emptied all NuGet caches for all versions, I searched the entire file system on all drives on my dev PC for AngleSharp, and hardcore deleted anything I could find. There are no files with AngleSharp in the name, and there are no files containing the text AngleSharp in them that explorer or FileSeek will find. So I reopened the project (in VS17 this time), tried a build... Error, can't restore AngleSharp 0.12.0.0... Tried opening it in VS Code, it reports the same error. I have NO idea where the reference is! – Rune Jacobsen May 14 '19 at 18:18
  • Just to be specific. There is nothing in the csproj file referencing AngleSharp. There is nothing in packages.config referencing AngleSharp. Nuking the NuGet cache has made it angry, but am still unable to locate whatever it is that tells VS (Code) that this package is needed.. – Rune Jacobsen May 14 '19 at 18:21
  • @RuneJacobsen Sorry you're still struggling with this. I'm pretty much out of ideas except for creating a new 4.7.2 solution with 2 projects, 1 class library and 1 web app, installing AngleSharp into that (try with a few versions) and seeing if you can reproduce it in a condensed way. – John H May 14 '19 at 18:41
  • John H No need to be sorry, you are not responsible for the situation and still trying to help, much appreciated. :) After a while I located project.assets.json which was not found earlier for some reason, cleaned it of all references to AngleSharp, and tried installing an older version. That didn't work because of API changes, so I tried moving up until I hit the CodePages redirect problem again. So at the very least I am back to the original issue - I will see if I can get the redirect to work on the old beast, then.. – Rune Jacobsen May 14 '19 at 19:50
3

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. :|

Rune Jacobsen
  • 9,907
  • 11
  • 58
  • 75
  • Well done for your persistence in getting to the bottom of it, and thanks for posting back here. – John H May 26 '19 at 10:12