Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shadow copying in order not to lock assembly

Tags:

c#

.net

clr

I have created a test library

public class Test
{
    public int Add(int val1, int val2)
    {
        return val1 + val2;
    }
}

And a project to call it :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace Loader
{
    class Program
    {
        static void Main(string[] args)
        {
            String path = @"...Lib.dll"; // path to lib
            var name = Path.GetFileName(path);

            AppDomainSetup setup = new AppDomainSetup  
            {
                ApplicationBase = @"...", // directory where Lib.dll is 
                ShadowCopyFiles = "true",
                ShadowCopyDirectories = @"..."// directory where Lib.dll is 
            };  

            var appdomain = AppDomain.CreateDomain("Loader." + name, null, setup);
            Assembly ass = Assembly.LoadFile(path); // <--- I think here is the problem, here where assembly is locked
            Assembly assembly = appdomain.Load(ass.FullName);

            dynamic a = assembly.CreateInstance("Lib.Test");

            Console.WriteLine(a.Add(1, 5));
        }
    }
}

Please help to find what I have done wrong ? Why my assembly is locked ?

EDIT : with hardcoded assembly name :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace Loader
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomainSetup ads = new AppDomainSetup();

            String fullPath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll";

            ads.ShadowCopyFiles = "true";
            ads.ApplicationName = "AppName";
            ads.ShadowCopyDirectories = Path.GetDirectoryName(fullPath);
            //ads.ApplicationBase = Path.GetDirectoryName(fullPath);
            //ads.PrivateBinPath = Path.GetDirectoryName(fullPath);
            ads.CachePath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Cache\";

            AppDomain ad = AppDomain.CreateDomain("myName" + ads.ApplicationName, null, ads);

            ad.AssemblyResolve += new ResolveEventHandler( ad_AssemblyResolve );

            Console.WriteLine(ad.ShadowCopyFiles);
            Console.WriteLine(ad.SetupInformation.ShadowCopyDirectories);

            try
            {
                //Assembly assembly = ad.Load(AssemblyName.GetAssemblyName(fullPath));
                //dynamic obj = ad.CreateInstanceAndUnwrap(assembly.GetName().Name, "Lib.Test");

                dynamic obj = ad.CreateInstanceAndUnwrap("Lib", "Lib.Test");

                Console.WriteLine(obj.Add(1, 7));

                Console.ReadKey();

                Console.WriteLine(obj.Add(1, 90));
            }
            catch( Exception e)
            {
                Console.WriteLine( e.Message );
            }
        }

        static Assembly ad_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFile(@"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll");
        }
    }
}

It is interesting that cached assebly is also locked.

EDIT 2:

Here is the line of code to block the assembly

Console.WriteLine(obj.Add(1, 7));

So once a method in Lib.Test is accessed the assenbly is blocked.

What can be the solution ?

like image 484
NDeveloper Avatar asked Jun 27 '11 16:06

NDeveloper


1 Answers

You're loading the assembly into your main AppDomain when you run Assembly.LoadFile(), which locks the file. If you want to load it directly into your AppDomain, you need to use AppDomain.Load() directly, allowing the AppDomain to resolve it based on the parameters you specified. Note that, since the assembly is not loaded in your main AppDomain, that type is inaccessible from your Main method. Your Lib.Test will need to derive from MarshalByRefObject so that the marshalling system can create a proxy (which further gets wrapped into a dynamic proxy when you assign it to dynamic.)


EDIT:

Upon further thought, I suspect the issue here is that, in order to call the method on the dynamic proxy, it has to Reflect on the object (which is itself a transparent proxy for the instance in your other AppDomain.) If it ends up reflecting over the original class type (and not the 'hidden' transparent proxy type), this will cause it to load a copy of the assembly in the local AppDomain (thus locking it.) This is one reason why, when using AppDomain isolation, you usually cast the object to an interface defined in a separate assembly that both the 'main' application and the hosted AppDomain can reference. They will each get their own copy of the interface assembly loaded, but now the 'implementation' assembly is isolated.

like image 90
Dan Bryant Avatar answered Sep 18 '22 08:09

Dan Bryant