My scenario is as follows:
This is a late answer but would be worthwhile to have it here for any future views to this question. I needed to implement something similar to this but in a dynamic code compilation/execution fashion. The best would be executing all methods in a separate domain, i.e.: remote domain, other than your main AppDomain, otherwise the app memory will always increase and increase. You can solve this problem via remote interfaces and proxies. So you would expose your methods through an interface which you will get an instance of in your main AppDomain and then remotely execute those methods in the remote domain, unload the newly created domain (remote domain), nullify it and then force the GC to collect unused objects. I spent quite a long time debugging my code until I reliazed that I have to force the GC to do so and it works just fine. Bulk of my implementation is taken from: http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm.
//pseudo code
object ExecuteCodeDynamically(string code)
{
Create AppDomain my_app
src_code = "using System; using System.Reflection; using RemoteLoader;
namespace MyNameSpace{
public class MyClass:MarshalByRefObject, IRemoteIterface
{
public object Invoke(string local_method, object[] parameters)
{
return this.GetType().InvokeMember(local_method, BindingFlags.InvokeMethod, null, this, parameters);
}
public object ExecuteDynamicCode(params object[] parameters)
{
" + code + } } } ";// this whole big string is the remote application
//compile this code which is src_code
//output it as a DLL on the disk rather than in memory with the name e.g.: DynamicHelper.dll. This can be done by playing with the CompileParameters
// create the factory class in the secondary app-domain
RemoteLoader.RemoteLoaderFactory factory =
(RemoteLoader.RemoteLoaderFactory)loAppDomain.CreateInstance("RemoteLoader",
"RemoteLoader.RemoteLoaderFactory").Unwrap();
// with the help of this factory, we can now create a real instance
object loObject = factory.CreateInstance("DynamicHelper.dll", "MyNamespace.MyClass", null);
// *** Cast the object to the remote interface to avoid loading type info
RemoteLoader.IRemoteInterface loRemote = (RemoteLoader.IRemoteInterface)loObject;
if (loObject == null)
{
System.Windows.Forms.MessageBox.Show("Couldn't load class.");
return null;
}
object[] loCodeParms = new object[1];
loCodeParms[0] = "bla bla bla";
try
{
// *** Indirectly call the remote interface
object result = loRemote.Invoke("ExecuteDynamicCode", loCodeParms);// this is the object to return
}
catch (Exception loError)
{
System.Windows.Forms.MessageBox.Show(loError.Message, "Compiler Demo",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Information);
return null;
}
loRemote = null;
try { AppDomain.Unload(my_app); }
catch (CannotUnloadAppDomainException ex)
{ String str = ex.Message; }
loAppDomain = null;
GC.Collect();//this will do the trick and free the memory
GC.WaitForPendingFinalizers();
System.IO.File.Delete("ConductorDynamicHelper.dll");
return result;
}
Note that RemoteLoader is another DLL that should be already created and added to both you main App and your remote App. It's basically an interface and a factory loader. The following code is taken from the above website:
///
/// Interface that can be run over the remote AppDomain boundary.
///
public interface IRemoteInterface
{
object Invoke(string lcMethod,object[] Parameters);
}
naemspace RemoteLoader{
///
/// Factory class to create objects exposing IRemoteInterface
///
public class RemoteLoaderFactory : MarshalByRefObject
{
private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
public RemoteLoaderFactory() {}
/// Factory method to create an instance of the type whose name is specified,
/// using the named assembly file and the constructor that best matches the specified parameters.
/// The name of a file that contains an assembly where the type named typeName is sought.
/// The name of the preferred type.
/// An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor.
/// The return value is the created object represented as ILiveInterface.
public IRemoteInterface Create( string assemblyFile, string typeName, object[] constructArgs )
{
return (IRemoteInterface) Activator.CreateInstanceFrom(
assemblyFile, typeName, false, bfi, null, constructArgs,
null, null, null ).Unwrap();
}
}
}
Hope this makes sense and helps...