Click here to Skip to main content
15,903,385 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
C#

Hi
I’ve been struggling for the past couple of days with at problem with AsssemblyResolve.
What I want to achieve is a setup for a service that can update and cache assemblies from a URL, and be able to Create and UnLoad an AppDomain without restarting the service.
But my problem is much simpler, and I have created a minimized sample, which illustrate my problem.
1: a console exe with a Loader

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

namespace TestAppDoms
{
    public static class Loader
    {
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += FindAssem;
            Program.GO();
        }

        public static Assembly FindAssem(object sender, ResolveEventArgs args)
        {
            string shortName = new AssemblyName(args.Name).Name;

            using (FileStream fs = File.OpenRead(Path.Combine(@"C:\TestAssem\", shortName + ".dll")))
            {
                byte[] assemBin = new byte[fs.Length];
                fs.Read(assemBin, 0, Convert.ToInt32(fs.Length));
                Assembly Assem = Assembly.Load(assemBin);

                return Assem;
            }
        }
    }

    public class Program
    {
        public static void GO()
        {
            brokerCtrl brkCtrl = new brokerCtrl();
            brkCtrl.Run();
            Console.ReadLine();
        }
    }
}


2: a dll (symbolizing the a cashed assembly)

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

namespace TestAppDoms
{
    public class brokerCtrl
    {
        public void Run()
        {
            Console.WriteLine("Hello From Ctrl.dll " + AppDomain.CurrentDomain.FriendlyName);
            
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            AppDomain brokerDom = AppDomain.CreateDomain("b2bBroker", AppDomain.CurrentDomain.Evidence, setup);
            brokerDom.AssemblyResolve += new ResolveEventHandler(brokerDom_AssemblyResolve);
 
            BrokerWrapper brokerProxy = (BrokerWrapper)brokerDom.CreateInstanceAndUnwrap(typeof(BrokerWrapper).Assembly.FullName, typeof(BrokerWrapper).FullName);
            brokerProxy.StartBroker();
            AppDomain.Unload(brokerDom);
            brokerProxy = null;
            
        }

        public static Assembly brokerDom_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string shortName = new AssemblyName(args.Name).Name;
            Console.WriteLine("Hello from brokerDom_AssemblyResolve");

            using (FileStream fs = File.OpenRead(Path.Combine(@"C:\TestAssem\", shortName + ".dll")))
            {
                byte[] assemBin = new byte[fs.Length];
                fs.Read(assemBin, 0, Convert.ToInt32(fs.Length));
                Assembly Assem = Assembly.Load(assemBin);
                return Assem;
            }
        }
    }

    public class BrokerWrapper : MarshalByRefObject
    {
        public override object InitializeLifetimeService()
        {
            return null;
        }
        public void StartBroker()
        {
            Console.WriteLine("Hello From " + AppDomain.CurrentDomain.FriendlyName);
        }

    }
}


When both TestAppDoms.exe and brokerCtrl.dll are placed in the same directory (and the CLR can locate the dll in the probing path) everything works like a charm.
But when I move the dll away from the exe (to the C:\TestAssem\ ) The brokerDom.AssemblyResolve += new ResolveEventHandler(brokerDom_AssemblyResolve) never fires - as expected.

Obviously I overlook something.

Can anybody help me solve my problem?

Best, Robert
Posted

1 solution

Finally I found a solution. (by reading Albahari C# in a NutShell)

First I defined a small assemby that both appdomains share.


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

namespace TestAppDoms
{
    public interface Ib2bBroker
    {
        void StartBroker();
    }

    public class b2bBrokerLink : MarshalByRefObject
    {
        public static Dictionary<string, Assembly> assemCache;
        static Dictionary<string, byte[]> assemBinCache;

        public b2bBrokerLink()
        {
            assemCache = new Dictionary<string, Assembly>();
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }
        public void InitAssemCache(Dictionary<string, byte[]> _assemBins)
        {
            assemBinCache = _assemBins;
            Console.WriteLine("Assems loaded");
        }

        public static Assembly brokerDom_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string shortName = new AssemblyName(args.Name).Name;
            Console.WriteLine("Hello from brokerDom_AssemblyResolve");

            if (assemCache.ContainsKey(shortName))
                return assemCache[shortName];

            if (assemBinCache.ContainsKey(shortName))
            {
                Assembly assem = Assembly.Load(assemBinCache[shortName]);
                assemCache.Add(shortName,assem);
                return assem;
            }
            return null;
        }
    }
}


Next modified BrokerCtrl.Run() as follows
C#
Console.WriteLine("Hello From New Ctrl.dll " + AppDomain.CurrentDomain.FriendlyName);

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
AppDomain brokerDom = AppDomain.CreateDomain("b2bBroker", AppDomain.CurrentDomain.Evidence, setup);
brokerDom.AssemblyResolve += new ResolveEventHandler(b2bBrokerLink.brokerDom_AssemblyResolve);

b2bBrokerLink assemResolver = (b2bBrokerLink)brokerDom.CreateInstanceFromAndUnwrap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "b2bBrokerLink.dll"), "TestAppDoms.b2bBrokerLink");
Dictionary<string,> assemBinCache = new Dictionary<string,>();
using (FileStream fs = File.OpenRead(Path.Combine(@"C:\TestAssem\", "b2bBroker" + ".dll")))
{
    byte[] assemBin = new byte[fs.Length];
    fs.Read(assemBin, 0, Convert.ToInt32(fs.Length));
    assemBinCache.Add("b2bBroker", assemBin);
}
assemResolver.InitAssemCache(assemBinCache);
Ib2bBroker broker = (Ib2bBroker)brokerDom.CreateInstanceAndUnwrap("b2bBroker", "TestAppDoms.b2bBroker");
broker.StartBroker();
AppDomain.Unload(brokerDom);
assemResolver = null;


Initiating b2bBrokerLink creates the new b2bBroker AppDomain and allows the main AppDomain download the most recent assembly-versions (as raw binary) and push assemCache (Direcrory<string,byte[]>) into the b2bBroker AppDomain.
Next start the a broker using the inteface Ib2bBroker and the brokerDom_AssemblyResolve will now be able to load the (most recent) assemblies into the new AppDomain.
It might be usefull for someone else - and maybe someone has a better solution.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900