1

I would like to load assemblies from a folder that is not my current app domain base folder. Some of the assemblies that I would like to load are already loaded in an older version in default app domain. In order to run this I found the following code. The new loaded assemblies are located in folder c:\testa\

How to Load an Assembly to AppDomain with all references recursively?

class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = @"c:\testa\";
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
}

public class Proxy : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFile(assemblyPath);
        }
        catch (Exception)
        {
            return null;
            // throw new InvalidOperationException(ex);
        }
    }
}

As the author of the code mentioned I get a file or dependency not found. So how can I handle this problem. Any idea?

Michael
  • 189
  • 1
  • 1
  • 11

2 Answers2

1

.NET Framework allows for full control by code of assembly resolution during their loading to Application Domain. It is achieved by using event handler attached to AppDomain.AssemblyResolve event. The role of the event handler is to load assembly and return Assembly instance. The way it is done is up to implementer as long as the loaded assembly will be ready for execution. It is possible for instance to generate new assembly on the fly andafter loading it returning a reference to it. It seems that use of thet method should solve the problem, as this event will fire for main module, sattelite assemblies, resource assemblies and all dependencies.

static void Main(string[] args)
{
    AppDomainSetup domaininfo = new AppDomainSetup();
    domaininfo.ApplicationBase = @"c:\testa\";
    Evidence adevidence = AppDomain.CurrentDomain.Evidence;
    AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);
    AssemblyResolver resolver = new AssemblyResolver();
    domain.Assembly += resolver.Resolve;

    Type type = typeof(Proxy);
    var value = (Proxy)domain.CreateInstanceAndUnwrap(
        type.Assembly.FullName,
        type.FullName);

    var assembly = value.GetAssembly(args[0]);
    // AppDomain.Unload(domain);
}

Implementation of Resolve instance method of more complex AssemblyResolver class with hard coded assembly data, in production these data are retrieved from main application assembly configuration file:

public Assembly Resolve(object sender, ResolveEventArgs e)
{

    System.Diagnostics.Debug.WriteLine("AssemblyResolve event for: {0}\t\tRequestingAssembly {1}", e.Name, e.RequestingAssembly);

    switch(e.Name)
    {
        case "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=aed0ab2de30e5e00":
            return LoadAssembly(e);
        case "System.Data.SQLite.EF6, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139":
            return LoadAssembly(e);
        case "BlueTechZone.Sqlite.EF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7741556173e269b9":
            string codeBase = CodeBase + _SubDirectory + "System.Data.SQLite.EF6.dll";
            var ef6SQLite = Assembly.LoadFrom(codeBase);
            return LoadAssembly(e, new String[] { "SQLite.Interop.dll" });
        case "System.Data.SQLite, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139":
            return LoadAssembly(e, new String[] { "SQLite.Interop.dll" });
        default:
            return null;
    }
}

Below is the code which is used to attach resolver to the very same domain in which it will be running before remaining part of application will be loaded into domain. APP_CONFIG_FILE is used to set path to configuration file used by application domain.

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", @"E:\src\**\<assembly name>.dll.config");

AssemblyResolver = new AssemblyResolver();
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolver.Resolve;
Jacek Blaszczynski
  • 3,183
  • 14
  • 25
0

I fixed the problem because I changed the flow of the program.

At first I copy the dlls from my current directory to the directory c:\testa. Existing files are not overwritten.

Then I start the program:

class Program
{
    static void Main(string[] args)
    {
        /* Copy files from current folder to c:\testa\ */

        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = @"c:\testa\";
        domaininfo.PrivateBinPath = @"c:\testa\";
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.Run(() => { /* My Program */});

    }
}

public class Proxy : MarshalByRefObject
{
    public void GetAssembly(Action a)
    {
        a();
    }
}

So I run the program in the new appdomain and all Assembly are loaded correctly.

Michael
  • 189
  • 1
  • 1
  • 11