0

I have a C#.NET class library, ClassLib, which uses both Newtonsoft.Json and RestSharp.

The idea of the library is that it is an API "super-wrapper" that is included in third-party end-user applications, eg desktop apps, web apps, Windows services, etc.

In order to keep things self-contained, I've embedded Newtonsoft and RestSharp in ClassLib using the embedded resources and AssemblyResolve approach discussed in several threads on SO.

So far, so banal; I've used this approach several times in different projects such as plug-ins and everything generally works as expected.

This time, however, I've hit a weird snag. And it's inconsistent.

I use ClassLib in a couple of small desktop app projects which demonstrate ClassLib's capabilities as well as a main production project.

Everything works perfectly in the demo projects, and was working fine in the production project. But the production project has just started throwing an assembly not found error:

"Could not load file or assembly 'RestSharp, Version=100.0.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75' or one of its dependencies. General Exception (Exception from HRESULT: 0x80131500)":"RestSharp, Version=100.0.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75"

I've tried to get the demo project to throw this error by running it with ClassLib.dll on four other machines which don't have RestSharp registered anywhere I can find but it runs perfectly.

But no matter what I do with the production project - which is running on a machine where there are multiple extraneous copies of RestSharp but nothing in the GAC that I can find - it continues to fail on this error.

The thing is, though, it was fine until yesterday.

So, of course, something has changed somewhere to create the issue, I just don't know what it is.

Pertinent points:

  1. ClassLib is obfuscated using Obfuscar. This has always been the case and nothing (that I can pinpoint) has changed.
  2. ClassLib, the demo projects and the production project all target .NET 4.5, as do the NuGet Newtonsoft and RestSharp packages.
  3. Everything builds fine; the error is run-time on first call to the API.
  4. If I include RestSharp as a reference assembly in the production project, all is well.
  5. I've double-checked assembly versions all over the place.
  6. I've cleared caches and even opened the project in other versions of VS (I use VS2017 Community but was using VS 2013 Pro until recently).
  7. Yes, I realize I could simply include RestSharp as a ClassLib dependency but I'm reticent to do that given that the embedded approach should work fine.
  8. I've also tried using Costura - which seems to work really well as far as I can tell - but the symptoms are the same.

Any ideas?

Parrish Husband
  • 3,148
  • 18
  • 40
SteveCinq
  • 1,920
  • 1
  • 17
  • 22
  • I've used the Assembly Binding Log Viewer as outlined in [this answer](https://stackoverflow.com/a/4606063/119549) to debug assembly loading issues in the past and found that very helpful. – Jacob Sep 08 '18 at 00:28
  • 2
    Bitness hasn't unexpectedly changed has it? – Parrish Husband Sep 08 '18 at 00:32
  • Just in case it's related to your code, you should post how you're attempting to load the assembly. – Jacob Sep 08 '18 at 00:33
  • Can you use version control system to go back in time where exception didn't occur? Then look at commit changes when this issue started to appear. – apocalypse Sep 08 '18 at 00:42
  • @Jacob I've posted my `AssemblyResolve` code in my own answer. – SteveCinq Sep 08 '18 at 21:07

1 Answers1

0

Ok, got it.

Both ClassLib and the main production project ("Product") implement(ed*) AssemblyResolve:

    internal static Assembly GetMissingAssembly(
        object s, 
        ResolveEventArgs e)
    {
        Assembly oResult = null;
        if (!e.Name.ToLower().Contains("resources"))
        {
            string Name = e.Name; //e.Name is read-only
            if (moAssemblies != null && moAssemblies.Count > 0)
            {
                if (moAssemblies.ContainsKey(Name)) oResult = moAssemblies[Name];
                if (oResult == null) throw new Exception("Could not load assembly " + Name);
            }
        }
        return oResult;
    }

However, the demonstration projects didn't as they have no embedded assemblies to extract.

So what was happening was that when Product didn't find RestSharp, it called GetMissingAssembly, reasonably enough, and when it wasn't found in the assembly collection, threw the designated error.

However, if I simply continue through the handler returning a null result, the reference ends up being resolved within ClassLib - as it should be - and everything is fine.

Whatever changed, I don't know yet - and I mightn't bother looking, to be honest - but the solution is to either 1) don't throw an error and just return null, 2) ignore any search for RestSharp explicitly or, probably best, 3) check whether an embedded assembly itself embeds the missing assembly and exit knowing it will be resolved.

* As mentioned, I ended up moving to Costura in ClassLib rather than handling AssemblyResolve myself. I'll do the same in Product and see if this works or not and post back. I'd hope that the writers on Costura have thought of this scenario but we'll see.

PS Thanks to Parish Husband for suggesting I check bitness; I did find a couple of things I needed to correct, though that wasn't the issue.

SteveCinq
  • 1,920
  • 1
  • 17
  • 22