1

I am loading CalculatorDependency.dll to AppDomain which is dependency of Add class (implementation of IAdd from Calculator.Interface.dll) from Calculator.dll.

The thing is I don't want to put implementer dll (Calculator.dll) and it's dependency dll (CalculatorDependency.dll) at executing assembly location or in GAC. I want to load it dynamically from given locations respectively. So I am loading Interface implementer dll and it's dependency to AppDomain first to create an instance.

Here is my code !!

    static void Main(string[] args)
    {
        // Loading dependency dll into memory
        var dependency = string.Concat(Directory.GetCurrentDirectory(), @"\Implementations\CalculatorDependency.dll");
        var dependencyBytes = File.ReadAllBytes(dependency);

        // Loading implementer dll into memory
        var implementor = string.Concat(Directory.GetCurrentDirectory(), @"\Implementations\Calculator.dll");
        var implementorBytes = File.ReadAllBytes(implementor);

        // Adding dependency dll to AppDomain (CalculatorDependency.dll)
        AppDomain.CurrentDomain.Load(dependencyBytes);
        // Adding implementor dll to AppDomain (Calculator.dll)
        AppDomain.CurrentDomain.Load(implementorBytes);

        // Checking loaded assemblies and both above assemblies are exist in output
        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
        foreach (var a in loadedAssemblies)
        {
            Console.WriteLine(a.GetName().Name);
        }

        // Calling function to get an instance of IAdd as Calculator.Add class from Calculator.dll
        var obj = GetObject<IAdd>();

        // Object was resolved successfully but was failed at this line as SumNew is a dependent function on CalculatorDependency.dll
        Console.WriteLine(obj.SumNew(2, 2));
    }

    public static T GetObject<T>()
    {
        var t = typeof(T);

        var objects = (
            from assembly in AppDomain.CurrentDomain.GetAssemblies()
            from type in assembly.GetExportedTypes()
            where typeof(T).IsAssignableFrom(type) && !type.FullName.Equals(t.FullName)
            select (T)Activator.CreateInstance(type)
        ).ToArray();

        return objects.FirstOrDefault();
    }

Error:

An unhandled exception of type 'System.IO.FileNotFoundException' occurred in TestConsole.exe

Additional information: Could not load file or assembly 'CalculatorDependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Console output of loaded assemblies:

enter image description here

Can someone please help me out to find out what is wrong here? And even if CalculatorDependency.dll is loaded why it is still looking for a file to load again?

Jester
  • 56,577
  • 4
  • 81
  • 125
GDroid
  • 1,254
  • 1
  • 16
  • 36
  • The error suggests that CalculatorDependency can't find the DLLs it requires. I believe the DLLs it requires are still being looked at in the base folder, not the "Implementations" folder it is located in. I know you can specify different paths when creating an AppDomain, but not sure when it is already created. – TyCobb Apr 02 '14 at 23:15
  • Thanks for your comment TyCobb! No all the assembly including interface implementer and its dependencies are in Implementations folder. And I want to load it from there and not from executing assembly base path. If I put CalculatorDependency.dll at base path from where console app is executing it picks up that file and does not throw an error. But I do not want to do that. So I am reading and loading CalculatorDependency.dll from the Implementations location to AppDomain and it is loaded as well but still looking for the file at base location.!!? Any suggestion on that? – GDroid Apr 02 '14 at 23:27
  • References/Modules are lazily loaded as needed. You can load a DLL, but you won't know there is a missing reference until it actually is attempted to be used which is why you are seeing it loaded, but still failing. The problem is that AppDomains look for references in the folder specified at creation time (look at AppDomain.RelativePath and AppDomain.CreateDomain(...) for what I am talking about. *Find out what DLLs your Calculator DLLs depend on and attempt to load those too.* – TyCobb Apr 02 '14 at 23:34
  • I tried to do something like this and it fails!! var appDomain = AppDomain.CreateDomain("MyAppDomain", null, Directory.GetCurrentDirectory(), @"\Implementations", false); appDomain.Load(dependencyBytes); appDomain.Load(implementorBytes); – GDroid Apr 03 '14 at 01:52

2 Answers2

2

Finally I found solution for my problem.

This is what worked for me. Perfect and what I was looking for !! :)

static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

    var implementor = string.Concat(Directory.GetCurrentDirectory(), @"\Implementations\Calculator.dll");
    var implementorBytes = File.ReadAllBytes(implementor);

    AppDomain.CurrentDomain.Load(implementorBytes);

    Console.WriteLine(GetObject<IAdd>().SumNew(2, 2));
    Console.WriteLine(GetObject<IAdd>().SumNew(2, 5));
}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    var dependencyResolverBaseDirectory = string.Concat(Directory.GetCurrentDirectory(), @"\Implementations");

    return Directory.GetFiles(dependencyResolverBaseDirectory, "*.dll")
        .Select(Assembly.LoadFile)
        .FirstOrDefault(assembly => string.Compare(args.Name, assembly.FullName, StringComparison.CurrentCultureIgnoreCase) == 0);
}

public static T GetObject<T>()
{
    var t = typeof(T);

    var objects = (
        from assembly in AppDomain.CurrentDomain.GetAssemblies()
        from type in assembly.GetExportedTypes()
        where typeof(T).IsAssignableFrom(type) && (string.Compare(type.FullName, t.FullName, StringComparison.CurrentCultureIgnoreCase) != 0)
        select (T)Activator.CreateInstance(type)
    ).ToList();

    return objects.FirstOrDefault();
}
GDroid
  • 1,254
  • 1
  • 16
  • 36
0

It is a good idea to load the file in Memory stream and then using it.

     using (FileStream file = new FileStream(@"c:\temp\TestAccountScrubbing.dll", FileMode.Open, FileAccess.Read))
        {
            byte[] bytes = new byte[file.Length];
            file.Read(bytes, 0, (int)file.Length);
            ms.Write(bytes, 0, (int)file.Length);
        }
        ms.Seek(0, SeekOrigin.Begin);
        Assembly assembly = Assembly.Load(ms.ToArray());

        Type type = assembly.GetType("TestAccountScrubbing.AccountScubbing");
        object obj = Activator.CreateInstance(type);
        var returnValue =type.InvokeMember("Main",
            BindingFlags.Default | BindingFlags.InvokeMethod,
            null,
            obj,
            new object[] { "Hello World" });
        }
MB_18
  • 1,620
  • 23
  • 37
BobSpring
  • 69
  • 1
  • 1