开发者

Compiling code at runtime, loading into current appdomain but Type.GetType cant see it

开发者 https://www.devze.com 2023-01-02 23:24 出处:网络
Im compiling some code at runtime then loading the assembly into the current appdomain, however when i then try to do Type.GetType it cant find the type...

Im compiling some code at runtime then loading the assembly into the current appdomain, however when i then try to do Type.GetType it cant find the type...

Here is how i compile the code...

public static Assembly CompileCode(string code)
    {
        Microsoft.CSharp.CSharpCodeProvider provider = new CSharpCodeProvider();
        ICodeCompiler compiler = provider.CreateCompiler();
        CompilerParameters compilerparams = new CompilerParameters();
        compilerparams.GenerateExecutable = false;
        compilerparams.GenerateInMemory = false;

        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                string location = assembly.Location;
                if (!String.IsNullOrEmpty(location))
                {
                    compilerparams.ReferencedAssemblies.Add(location);
                }
            }
            catch (NotSupportedException)
            {
                // this happens for dynamic assemblies, so just ignore it. 
            }
        } 
        CompilerResults results =
           compiler.CompileAssemblyFromSource(compilerparams, code);
        if (results.Errors.HasErrors)
   开发者_StackOverflow中文版     {
            StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
            foreach (CompilerError error in results.Errors)
            {
                errors.AppendFormat("Line {0},{1}\t: {2}\n",
                       error.Line, error.Column, error.ErrorText);
            }
            throw new Exception(errors.ToString());
        }
        else
        {
            AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
            return results.CompiledAssembly;
        }
    }

This bit fails after getting the type from the compiled assembly just fine, it does not seem to be able to find it using Type.GetType....

Assembly assem = RuntimeCodeCompiler.CompileCode(code);
string typeName = 
      String.Format("Peverel.AppFramework.Web.GenCode.ObjectDataSourceProxy_{0}",
        safeTypeName);



Type t = assem.GetType(typeName); //This works just fine..
Type doesntWork = Type.GetType(t.AssemblyQualifiedName);
Type doesntWork2 = Type.GetType(t.Name);



....


Found this nice bit of code that ensures no matter how you load your assembly it is always available from Type.GetType.

My class for compiling code into the current appdomain now looks like :

public static class RuntimeCodeCompiler
{
    private static volatile Dictionary<string, Assembly> cache = new Dictionary<string, Assembly>();
    private static object syncRoot = new object();
    static Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
    static RuntimeCodeCompiler()
    {
        AppDomain.CurrentDomain.AssemblyLoad += (sender, e) =>
        {
            assemblies[e.LoadedAssembly.FullName] = e.LoadedAssembly;
        };
        AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
        {
            Assembly assembly = null;
            assemblies.TryGetValue(e.Name, out assembly);
            return assembly;
        };

    }


    public static Assembly CompileCode(string code)
    {

        Microsoft.CSharp.CSharpCodeProvider provider = new CSharpCodeProvider();
        ICodeCompiler compiler = provider.CreateCompiler();
        CompilerParameters compilerparams = new CompilerParameters();
        compilerparams.GenerateExecutable = false;
        compilerparams.GenerateInMemory = false;



        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                string location = assembly.Location;
                if (!String.IsNullOrEmpty(location))
                {
                    compilerparams.ReferencedAssemblies.Add(location);
                }
            }
            catch (NotSupportedException)
            {
                // this happens for dynamic assemblies, so just ignore it. 
            }
        } 


        CompilerResults results =
           compiler.CompileAssemblyFromSource(compilerparams, code);
        if (results.Errors.HasErrors)
        {
            StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
            foreach (CompilerError error in results.Errors)
            {
                errors.AppendFormat("Line {0},{1}\t: {2}\n",
                       error.Line, error.Column, error.ErrorText);
            }
            throw new Exception(errors.ToString());
        }
        else
        {

            AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
            return results.CompiledAssembly;
        }

    }

    public static Assembly CompileCodeOrGetFromCache(string code,  string key)
    {
        bool exists = cache.ContainsKey(key);

        if (!exists)
        {

            lock (syncRoot)
            {
                exists = cache.ContainsKey(key);

                if (!exists)
                {
                    cache.Add(key, CompileCode(code));
                }
            }
        }

        return cache[key];
    }


}


You should read a bit more about assembly loading, type resolution, ... I don't know exactly why your code works at all, but I guess you have the following problem:

You compile the assembly and then you call AppDomain.CurrentDomain.Load to load the assembly. But you do not return the assembly you have just loaded. You return the assembly from the compilation result. Now you have two instances of the same assembly and you have each type from that assembly twice. These pairs have the same names, but they are not the same types!

0

精彩评论

暂无评论...
验证码 换一张
取 消