Как я могу использовать CodeDOM для создания и загрузки сборки в AppDomain?

Я работаю над проектом, который будет использовать CodeDOM для создания класса, который оценивает определяемое пользователем выражение, создает сборку для класса и загружает сборку. Поскольку может быть достаточное количество определяемых пользователем выражений, я хотел бы сначала создать домен приложения, выполнить создание / загрузку и выполнение CodeDOM для сборки в этом домене приложения, а затем выгрузить домен приложения.

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

В этом примере (DynamicCode) создается сборка с использованием CodeDOM, а затем загружается в AppDomain, однако автор создает сборку на диск. Я бы предпочел сгенерировать сборку в памяти, чтобы мне не приходилось управлять очисткой сгенерированных сборок. (хотя при этом создается .dll во временной папке).

Может ли кто-нибудь указать мне на пример того, как это сделать?

Любая помощь будет принята с благодарностью.

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

private string CreateSource()
{
    CodeCompileUnit codeUnit = new CodeCompileUnit();
    CodeNamespace codeNamespace = new CodeNamespace(Namespace);
    CodeTypeDeclaration codeClass = new CodeTypeDeclaration
    {
        Name = "ExpressionEvaluator",
        IsClass = true,
        TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed
    };

    codeNamespace.Types.Add(codeClass);
    codeUnit.Namespaces.Add(codeNamespace);

    AddMethods(codeClass);

    string result = GenerateSourceCode(codeUnit);

    return result.ToString();
}

private CompilerResults CompileSource(string source)
{
    using (CodeDomProvider provider = new CSharpCodeProvider())
    {
        CompilerParameters parameters = CreateCompilerParameters();
        CompilerResults result = CompileCode(provider, parameters, source);

        return result;
    }
}

private static CompilerParameters CreateCompilerParameters()
{
    CompilerParameters result = new CompilerParameters
    {
        CompilerOptions = "/target:library",
        GenerateExecutable = false,
        GenerateInMemory = true
    };

    result.ReferencedAssemblies.Add("System.dll");

    return result;
}

private object RunEvaluator(CompilerResults compilerResults)
{
    object result = null;
    Assembly assembly = compilerResults.CompiledAssembly;

    if (assembly != null)
    {
        string className = "ExpressionEvaluator";
        object instance = assembly.CreateInstance("Lab.ExpressionEvaluator");

        Module[] modules = assembly.GetModules(false);

        Type type = (from t in modules[0].GetTypes()
                     where t.Name == className
                     select t).FirstOrDefault();

        MethodInfo method = (from m in type.GetMethods()
                             where m.Name == "Evaluate"
                             select m).FirstOrDefault();

        result = method.Invoke(instance, null);
    }
    else
    {
        throw new Exception("Unable to load Evaluator assembly");
    }

    return result;
}

Я считаю, что эти фрагменты кода демонстрируют базовую функциональность моего проекта. Теперь все, что мне нужно сделать, это обернуть его в отдельный домен приложения.


person Welton v3.60    schedule 18.08.2010    source источник


Ответы (4)


Я нашел ответ, который искал, на http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html. У него есть хорошая статья, в которой подробно описывается создание AppDomain и загрузка сборки как плагина. Я последовал его примеру и смог создать AppDomain, создать прокси для моей фабрики классов ExpressionEvaluator, успешно вызвать его и получить результаты.

person Welton v3.60    schedule 19.08.2010
comment
Теперь это мертвая ссылка. - person David L; 27.10.2015

Проблема с курицей и яйцом. Вам понадобится небольшая сборка загрузчика, которую вы можете загрузить в новый домен приложений. С хорошо известным классом, который загружает сгенерированную CodeDom сборку и запускает ее.

Делать это с помощью GenerateInMemory довольно бессмысленно, вам придется сериализовать это в новый AppDomain. Это просто куча накладных расходов, с таким же успехом можно загрузить его с диска, в любом случае он там есть. И это уже в памяти. Кеш-память файловой системы. Загрузка будет очень быстрой, так как на самом деле ее не нужно читать с диска.

person Hans Passant    schedule 18.08.2010

Просто используйте AssemblyBuilderAccess.Run при определении динамической сборки http://msdn.microsoft.com/en-us/library/system.reflection.emit.assemblybuilderaccess.aspx

Динамическую сборку можно выполнить, но нельзя сохранить.

person Joel Martinez    schedule 18.08.2010
comment
Я считаю, что вы путаете Reflection.Emit с CodeDom. - person Kirk Woll; 18.08.2010
comment
Спасибо, Джоэл. Смотрю в динамическую сборку. - person Welton v3.60; 18.08.2010

Откуда во всем этом метод CompilCode? Кажется, это самая важная часть, и вы решили ее не учитывать?

person Ray    schedule 07.10.2012
comment
Метод CompileCode на самом деле не имеет отношения к проблеме, которая заключалась в том, как сгенерировать исходный код, создать сборку и загрузить ее в AppDomain во время выполнения. Я понимаю, что мой первоначальный вопрос, возможно, не был полностью точным описанием моей проблемы, но по мере того, как я получил отзывы и провел дополнительные исследования, он уточнил объем и определение проблемы. В частности, порядок действий, которые я пытался выполнить. Статья в принятом ответе прояснила подход, который мне нужно было использовать, и помогла мне решить мою проблему. - person Welton v3.60; 12.11.2012