Компиляция кода во время выполнения, загрузка в текущий домен приложения, но Type.GetType не может его увидеть - PullRequest
5 голосов
/ 11 июня 2010

Я компилирую некоторый код во время выполнения, затем загружаю сборку в текущий домен приложения, однако, когда я тогда пытаюсь сделать Type.GetType, он не может найти тип ...

Вот как я скомпилировал код ...

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;
        }
    }

Этот бит завершается ошибкой после получения типа из скомпилированной сборки, он не может найти его с помощью 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);



....

Ответы [ 2 ]

8 голосов
/ 11 июня 2010

Найдено этот хороший фрагмент кода, который гарантирует, что независимо от того, как вы загружаете сборку, он всегда доступен из Type.GetType.

Мой класс для компиляции кода в текущий домен приложения теперь выглядиткак:

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];
    }


}
0 голосов
/ 11 июня 2010

Вы должны прочитать немного больше о загрузке сборки, разрешении типов, ... Я не знаю точно, почему ваш код работает вообще, но я думаю, у вас есть следующая проблема:

Вы компилируете сборку и затем вызываете AppDomain.CurrentDomain.Load для загрузки сборки. Но вы не вернете только что загруженную сборку. Вы возвращаете сборку из результата компиляции. Теперь у вас есть два экземпляра одной сборки, и у вас есть каждый тип из этой сборки дважды. Эти пары имеют одинаковые имена, но они не одного типа!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...