Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use .NET assembly without locking dll file

I have a windows service that executes once a day. It's only 10 minutes of action.

I build the service's files in the bin folder of the website, because the service uses the website dll's.

Unfortunately I noticed that when I installed the website module (dotnetnuke .zip module by website installer) I got an error that the file is locked by antoher process.

Disabling the service before each module is installed is very problematic so I want to make my service not lock the dll's which are used in it.

I read about the AppDomain and ShadowCopyFiles options, but I can NOT make them work for me.

This is an example of how I use ShadowCopyFiles:

class Program
{
    static string GenerateRandomName()
    {
        //maybe name must be unique?!
        var random = new Random();
        return "a" + random.Next().ToString() + "b" + random.Next();
    }

    static int RandomNumber()
    {
        var info = ConfigureAppDomainSettings();

        var domain = AppDomain.CreateDomain(GenerateRandomName(), null, info);
        var assembly = domain.Load("SharedLibrary");
        var shared = (SharedLibrary.IShared)assembly.CreateInstance("SharedLibrary.Shared");

        int retVal = shared.RandomNumber();

        AppDomain.Unload(domain);

        domain = null;
        assembly = null;
        shared = null;
        info = null;

        Console.WriteLine("before cleanning");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForFullGCComplete();
        GC.Collect();
        Console.WriteLine("after cleanning");

        return retVal;
    }

    static AppDomainSetup ConfigureAppDomainSettings()
    {
        AppDomainSetup domainSettings = AppDomain.CurrentDomain.SetupInformation;
        domainSettings.ShadowCopyFiles = "true";
        domainSettings.CachePath = @"C:\inetpub\wwwroot\eshop\DesktopModules\Rossnet_EShop\SharedAssemblyTest\PrivateCache";

        return domainSettings;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(RandomNumber());

        Console.ReadKey();
    }
}

Code of example assembly:

public interface IShared
{
    int RandomNumber();
}

[Serializable]
public class Shared : IShared
{
    public int RandomNumber()
    {
        var random = new Random();
        return random.Next(20);
    }
}

A private cache is created and not locked and the original file is locked. <- right now this is absurd for me.

like image 523
Emil Jasiński Avatar asked Jul 21 '14 20:07

Emil Jasiński


1 Answers

If you have access to modify all code that uses the assemblies, simply loading them by the Assembly.Load overload that takes a byte[] should do the trick. You'll read the file yourself, and pass it to that method. I use this approach myself (for other reasons though), and it gives me the ability to update the files at any time.

A way of doing this easily (albeit a bit weird) is to rename your files (from .dll to something else, so they can't be found) and then subscribe to the AppDomain.AssemblyResolve event, which will be called when they're missing, at which point you can have your code that manually loads the assemblies.

like image 80
Chris Avatar answered Oct 23 '22 12:10

Chris