I have an expression evaluator project in C# that generates IL and converts the IL into an evaluation methods. The issue is that, there is no way to unload an assembly from C# and the compiler keeps on adding the assemblies to the current app context every time I call the expression evaluation. One thing I could do to avoid this is to create a appdomain, generate the IL within the appdomain, and kill the appdomain after IL is generated.
Here is the sample code that does this. Could someone tell me the efficient way of dealing with the multiple assembly loading issue?
DynamicMethodState methodState;
// Use CodeDom to compile using C#
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters loParameters = new CompilerParameters();
// Add assemblies
loParameters.ReferencedAssemblies.Add("System.dll");
loParameters.ReferencedAssemblies.Add(functionType.Assembly.Location);
// Don't generate assembly on disk and treat warnings as errors
loParameters.GenerateInMemory = true;
loParameters.TreatWarningsAsErrors = true;
// Set name space of the dynamic class.
string dynamicNamespace = "ExpressionEval.Functions.Dynamic";
string source = @"
using System;
using {5};
namespace {6}
{{
public class {0} : {1}
{{
public {2} {3}()
{{
return {4};
}}
}}
}}
";
// Set source code replacements
string className = "Class_" + Guid.NewGuid().ToString("N");
string methodName = "Method_" + Guid.NewGuid().ToString("N");
string returnTypeName = returnType.FullName;
// Check for generic type for return
if (returnType.IsGenericType)
{
// Check for null-able
Type genericType = returnType.GetGenericTypeDefinition();
if (genericType == typeof(Nullable<>))
{
// Nullable so add ?
Type nullableType = Nullable.GetUnderlyingType(returnType);
returnTypeName = nullableType.FullName + "?";
}
else
{
// Not nullable but is generic so get the list of types
Type[] genericArgTypes = returnType.GetGenericArguments();
// Get type name without the last 2 characters for generic type names
returnTypeName = genericType.FullName.Substring(0, genericType.FullName.Length - 2) + "<";
// Loop through type arguments and build out return type
foreach (Type genericArgType in genericArgTypes)
{
returnTypeName += genericArgType.FullName;
}
// Add ending generic operator
returnTypeName += ">";
}
}
// Format code string with replacements.
string codeString = string.Format(CultureInfo.InvariantCulture, source, className, functionType.FullName, returnTypeName, methodName, expression, functionType.Namespace, dynamicNamespace);
// Compile the code.
CompilerResults results = codeProvider.CompileAssemblyFromSource(loParameters, codeString);
if (results.Errors.Count > 0)
{
// Throw an exception for any errors.
throw new ApplicationException("Compile of policy failed.");
}
else
{
// Get the type that was compiled.
Type dynamicType = results.CompiledAssembly.GetType(dynamicNamespace + "." + className);
// Get the MethodInfo for the compiled expression.
MethodInfo dynamicMethod = dynamicType.GetMethod(methodName);
// Get the compiled expression as serializable object.
methodState = GetMethodState(dynamicMethod);
}
return methodState;