Search code examples
c#wpfdllassembliesnancy

'Could not load assembly', but the assembly is already loaded


I am working on a simple WPF app which makes use of two external DLLs, Nancy.dll and Nancy.Hosting.Self.dll to send some data over http. I'd like to keep the .exe file standalone so I'm trying to merge the two .dll files into the application. I tried multiple post-build merging methods such as NetZ and ILMerge, but both seem to have problems with wpf applications and didn't output a working executable.

There is this post which has multiple suggestions for going about this problem, although they all boil down to the same two things:

  1. Use a post-build merger. This doesn't work well in my case.
  2. Put the DLL into the application as an embedded resource, and make use of the AppDomain.CurrentDomain.AssemblyResolve event to load it when necessary.

The second option seemed promising: the event gets triggered, it finds the embedded resource, makes a datastream out of it and loads it as an assembly. I can verify the assembly is loaded in multiple ways:
Visual Studio's Debug -> Windows -> Modules shows the loaded assembly, and
AppDomain.CurrentDomain.GetAssemblies() also shows the assembly being loaded.

However, when it comes to using the assemblies (in this case calling Nancy.Hosting.Self.NancyHost host = new NancyHost();) I still get the following error:

Could not load file or assembly 'Nancy, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

I did make sure all dependencies are also loaded (as suggested here) and all loaded assemblies are in the same AppDomain. Also note that the above error is only for Nancy.dll, the AssemblyResolve event does manage to load Nancy.Hosting.Self.dll correctly.

I really don't see what else I might be doing wrong, and whether something goes wrong with me loading the assemblies or whether is specifically Nancy which is behaving strangely (I found this issue on GitHub of which I'm uncertain whether it's related). If you have any suggestions as to loading the assemblies, merging the dll's, or an alternative to Nancy, I'd be very happy to hear.

P.S.:
I tried calling different methods from Nancy.dll, and to my surprise I was able to; the AssemblyResolve method does actually work.
Initializing a Nancy.Hosting.Self.NancyHost is still problematic though, and the stack trace hints at why:

   at System.AppDomain.CreateInstanceAndUnwrap(String assemblyName, String typeName)
   at Nancy.AppDomainAssemblyCatalog.CreateRemoteReferenceProber(AppDomain appDomain)
   at Nancy.AppDomainAssemblyCatalog.LoadNancyReferencingAssemblies(IEnumerable`1 loadedAssemblies)
   at Nancy.AppDomainAssemblyCatalog.GetAvailableAssemblies()
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at Nancy.DefaultTypeCatalog.GetTypesAssignableTo(Type type)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Nancy.DefaultTypeCatalog.GetTypesAssignableTo(Type type, TypeResolveStrategy strategy)
   at Nancy.Bootstrapper.NancyBootstrapperLocator.GetBootstrapperType(ITypeCatalog typeCatalog)
   at Nancy.Bootstrapper.NancyBootstrapperLocator.LocateBootstrapper()
   at Nancy.Bootstrapper.NancyBootstrapperLocator.get_Bootstrapper()

So, apparently, when Nancy initializes its bootstrapper, it tries to get its referencing assemblies and uses the method GetAssemblyDirectories for this. Naturally, the DLLs aren't present there (as I'm trying to merge them into the .exe) and the bootstrapper fails to initialize.

As I'm not going to be able to fix this, and Nancy is no longer being maintained, I'd like to restate my question:
Does anybody know of a nice lightweight web framework for c#?


Solution

  • I didn't really want to go and look for something else, as Nancy's just working fine for me. What I ended up doing instead was writing the dll to where I knew nancy was going to look for it, initialize that bootstrapper, and remove the data again:

    bool NancyPresent = File.Exists("Nancy.dll");
    if (!NancyPresent) {
        var assembly = Assembly.GetExecutingAssembly();
        using (Stream stream = assembly.GetManifestResourceStream("Resources.Nancy.dll"))
        using (MemoryStream MS = new MemoryStream()) {
            stream.CopyTo(MS);
            File.WriteAllBytes("Nancy.dll", MS.ToArray());
        }
        Nancy.Bootstrapper.INancyBootstrapper bootstrapper = Nancy.Bootstrapper.NancyBootstrapperLocator.Bootstrapper;
        bootstrapper.Initialise();
        File.Delete("Nancy.dll");
    }
    

    The takeaway-message is that the dll was being loaded fine, despite Nancy's errors indicating otherwise. Even if it's open-source, you never know what exactly third-party packages are going to do!