0

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;
Michael
  • 57,169
  • 9
  • 80
  • 125
Steelbird
  • 49
  • 6
  • 1
    Which assemblies are steadily being added? Your dynamic generated ones? Or the ones you add to `loParameters.ReferencedAssemblies`? – Oliver Nov 06 '14 at 09:36
  • 1
    This is a *very* common need. Just don't write your own, google "c# expression evaluator". – Hans Passant Nov 06 '14 at 11:36
  • `"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."` As far as I know, this is the only way to unload an assembly in .NET. So why not just do that? – Peter Duniho Nov 06 '14 at 18:29
  • I was asking about the dynamic generated assemblies. – Steelbird Nov 12 '14 at 08:12

0 Answers0