New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppDomainAssemblyTypeScanner locks DLL files #2123

Closed
hassanselim0 opened this Issue Nov 21, 2015 · 1 comment

Comments

Projects
None yet
3 participants
@hassanselim0

hassanselim0 commented Nov 21, 2015

I'm using Nancy to make a small service that gets called from a main ASP.NET MVC application, during testing I have the self-host Nancy EXE and the main web app's DLLs in the same folder, and I noticed that I can't rebuild the web app while the EXE is still running because the DLL files are locked (in-use).
After digging around for a long time I found the problem to be in AppDomainAssemblyTypeScanner.LoadAssembliesWithNancyReferences where it calls Assembly.ReflectionOnlyLoadFrom which effectively locks the DLL files.
I've looked around on how to solve this, and discovered that you can't unload an assembly and you have to unload the whole AppDomain instead, I tried to create a new AppDomain just for the sake of the assembly inspection and called AppDomain.Load instead of Assembly.ReflectionOnlyLoadFrom then unloaded this temp AppDomain but that didn't work, it seems that AppDomain.Load also loads the assembly into the current AppDomain.
So after more searching around I found this: http://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom
It seems that in order to do this correctly the inspection code has to run completely under the new AppDomain and the results marshalled back to the main AppDomain.

Now I'm almost completely new to the concepts of AppDomains and Marshalling, I know the concepts but never implemented anything that touches these areas. I could try to implement this technique into ADATS but I feel I don't have the skills to do it correctly, I'm also worried that it might cause performance issues.
So I'm asking for your opinions about this. Did anybody here think of this before? If yes, then why didn't you implement this?

@smaclell

This comment has been minimized.

Show comment
Hide comment
@smaclell

smaclell Jan 13, 2016

This is not a problem with Nancy and more of an issue with how .NET is loading DLLs.

The easiest solution is to create another executable to wrap your original one. In the new application you then create a separate AppDomain with a feature called "Shadow Copy" enabled. This will copy DLLs to a separate directory before loading them. You local DLLs will be left unlocked.

The code for the wrapper executable looks like this:

using System;

class Program {
    static int Main( string[] args ) {

        AppDomainSetup setup = new AppDomainSetup {
            ShadowCopyFiles = "true" // This is key
        };

        var domain = AppDomain.CreateDomain( "Real AppDomain", null, setup );

        // Execute your real application in the new app domain
        int result = domain.ExecuteAssembly(
            "Implementation.exe",
            args
        );

        return result;

    }
}

That is it! You do not need marshaling or anything fancy. For more information checkout this blog post I wrote about it. It has more code samples and links to a full solution.

smaclell commented Jan 13, 2016

This is not a problem with Nancy and more of an issue with how .NET is loading DLLs.

The easiest solution is to create another executable to wrap your original one. In the new application you then create a separate AppDomain with a feature called "Shadow Copy" enabled. This will copy DLLs to a separate directory before loading them. You local DLLs will be left unlocked.

The code for the wrapper executable looks like this:

using System;

class Program {
    static int Main( string[] args ) {

        AppDomainSetup setup = new AppDomainSetup {
            ShadowCopyFiles = "true" // This is key
        };

        var domain = AppDomain.CreateDomain( "Real AppDomain", null, setup );

        // Execute your real application in the new app domain
        int result = domain.ExecuteAssembly(
            "Implementation.exe",
            args
        );

        return result;

    }
}

That is it! You do not need marshaling or anything fancy. For more information checkout this blog post I wrote about it. It has more code samples and links to a full solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment