On Demand Assembly Load - Part I

 05-Feb-2012   NityaPrakash     .NET  Assembly  AppDomain    Comments  0

In this article I'll show you, how to load assembly dynamically on demand. Recently I worked on a project where I needed to load same assembly from different code base. Assemblies were located on different location as per the environment(dev/qa/uat/prod).

Also wanted to load them in different application domain, so they will work in their own domain and will not interfere in each other tasks.

Current AppDomain vs. New AppDomain

Current application domain is the AppDomain, where your current application is running. If you load assemblies in same AppDomain, they will be locked, and cannot be unloaded. It will remain there until you exit from the application. This situation fails the purpose of loading multiple assembly. If assembly loaded in different AppDomain will allow us to unload the AppDomain at any point of time and release memory.

Instantiating Class

If assembly loaded in same AppDomain, class can be instantiated usual way. But if assembly loaded in different AppDomain than, it can be instantiated using reflection. Another way is an interface. However this interface should be implemented in the assembly which being loaded dynamically.  Here I am going over second method where, I created a interface in a assembly which is being refereed by both assembly, current appdomain assembly and the assembly which I want to load dynamically.

I wanted to create a AddIn for outlook which push meeting from outlook to my application,  and wanted to allow push the meeting to different environment based on the option user select.  These options are available based on the user�s group.  I am not going to discuss on Outlook AddIn here.  I will discuss in other article dedicated to Outlook.

I created a Assembly AddInCommon where I created interface IMyAddIn and implemented in a MyAddIn abstract class.


public interface IMyAddin
{

	void ShowMessage();

	void Increment();

}

public abstract class MyAddIn: MarshalByRefObject, IMyAddin
{
        
	public abstract string ShowMessage();


	public abstract void Increment();
}

This class will be inherited in the class of the dynamically loaded assembly.


[Serializable]
public class MyClass 
{
	public int counter = 0;
        public string ShowMessage()
        {
            Console.WriteLine("Assembply Path : " + Assembly.GetExecutingAssembly().CodeBase);
            Console.WriteLine("MDLibOne.MyClass.ShowMessage");

            return "Assembply Path : " + Assembly.GetExecutingAssembly().CodeBase;
        }

        public void Increment()
        {
            Console.WriteLine("Counter : " + ++counter);

        }
}

MyClass is a Serializable because this class going to be used in the currentAppDomain, while its defined in the its own assembly.  Object is need to be serialized and desterilized back in the currentAppDomain.  Both application domains have different memory allocation and can�t share the object.  Create AppDomainSetup for each codebase you want to load.  Below example create the AppDomainSetup and store them in a dictionary, so I don�t do setup again create application domain each time when want to instantiate class.  This is also allow me to unload.

AssemblyLoader class

This class is responsible for manage the lifetime of AppDomains.


public IMyAddin GetAddIn(string name_)
        {
            if (!_addIns.ContainsKey(name_))
            {
                AppDomainSetup domainSetup = new AppDomainSetup();
                domainSetup.ApplicationName = Path.GetFileNameWithoutExtension(_codeBase[name_].ToString());
                //domainSetup.ConfigurationFile = Path.GetFileName(file.Value.ToString()) + ".Config";
                domainSetup.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

                AppDomain appDomain = AppDomain.CreateDomain(domainSetup.ApplicationName, null, domainSetup);

                _appDomains.Add(name_.ToString(), appDomain);

                IMyAddin addin = appDomain.CreateInstanceAndUnwrap(domainSetup.ApplicationName, 
					domainSetup.ApplicationName + ".MyClass") as MyAddIn;

                _addIns.Add(name_, addin);
            }

            return _addIns[name_];
        }

Calling the AddIn

 

         AssemblyLoader addInLoader = new AssemblyLoader();
	 //Load the assembly we need and instantiate to execute the method/property.
	IAddin addIn =	addInLoader.GetAddIn("One");
 	MessageBox.Show(addIn.GetMessage());

Wrapping Up

This example will show that it is loading from appropriate location.  But still it has an issue that object created has short time span.  Object created with this method will soon expire and will throw an exception.  Object will be timed out after some time.  Life time will be decided by source assembly. I am going to discuss this in next article.


Nitya Prakash Sharma has over 10 years of experience in .NET technology. He is currently working as Senior Consultant in industry. He is always keen to learn new things in Technology and eager to apply wherever is possible. He is also has interest in Photography, sketching and painting.

My Blog
Post Comment

COMMENTS