Выполнение кода, сгенерированного Roslyn

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

namespace GSCrm.Models.Custom.TableModels
{
    using System;
    using GSCrm.Models.Default.MainEntities;
    using GSCrm.Models.Default.TableModels;
    using System.Text.Json.Serialization;
    public class TestTable : MainTable
    {
    }
}

Затем этот класс записывается в файл, а дерево класса передается в метод, занимающийся компиляцией:

// Запись в файл
CodeGenUtils.WriteNode(compilationUnit.SyntaxTree, entityName, entityType);

// Компиляция
CodeGenUtils.CreateCompilation(compilationUnit.SyntaxTree);

Код метода компиляции:

public static CSharpCompilation CreateCompilation(SyntaxTree syntaxTree)
{
    Compilation = Compilation == null ? CSharpCompilation.Create(
        "GSCrm",
        new[] { syntaxTree }, new[]
        {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(JsonSerializer).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Program).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Database).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Migration).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Expression).Assembly.Location),
            MetadataReference.CreateFromFile(dllFolderPath + "System.Runtime.dll"),
            MetadataReference.CreateFromFile(dllFolderPath + "netstandard.dll")
        },
        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
    : Compilation.AddSyntaxTrees(syntaxTree);

    return Compilation;
}

После этого пользователь нажимает на кнопку "ApplyTable" и сущность добавляется в контекст приложения, по сути в класс контекста просто добавляется новая строка:

public DbSet<TestTable> TestTable { get; set; }

И снова происходит запись и компиляция:

// Запись в файл
CodeGenUtils.WriteNode(tree, "ApplicationContext", typeof(ApplicationContext));

// Компиляция
CodeGenUtils.CreateCompilation(tree);

Затем, необходимо произвести миграцию бд, для этого генерируется класс следующего вида:

namespace GSCrm.GenerateMigrations
{
    using System;
    using GSCrm.Data;
    using GSCrm.Models.Custom.TableModels;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.EntityFrameworkCore.Migrations;

    [DbContext(typeof(ApplicationContext))]
    [Migration("TestTable_2020410_221241")]
    public partial class TestTable_2020410_221241 : Migration
    {
        private readonly ApplicationContext context;

        public TestTable_2020410_221241() { }

        public TestTable_2020410_221241(ApplicationContext context)
        {
           this.context = context;
        }

        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "TestTable",
                columns: table => new
                {
                    CreatedBy = table.Column<string>(nullable: false),
                    Id = table.Column<Guid>(nullable: false),
                    UpdatedBy = table.Column<string>(nullable: false),
                    Updated = table.Column<DateTime>(nullable: false),
                    Created = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_TestTable", x => x.Id);
                });
        }

        public void Migrate()
        {
            context.Set<TestTable>();
            context.Database.Migrate();
        }
    }
}

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

using (MemoryStream stream = new MemoryStream())
{
    EmitResult emit = CodeGenUtils.Compilation.Emit(stream);
    stream.Seek(0, SeekOrigin.Begin);

    // Здесь ошибка
    if (emit.Success)
    {

    }
}

EmitResult содержит ошибку: Не удалось найти тип или имя пространства имен "TestTable" (возможно, отсутствует директива using или ссылка на сборку). При этом если выполнить emit до добавления сущности в контекст приложения и до миграции, такой ошибки не будет. Может быть кто-нибудь знает, в чем причина


Ответы (1 шт):

Автор решения: Гасан Гаджинский

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

Там для генерации namespace-а я использовал такой код:

NamespaceDeclaration(IdentifierName("GSCrm.Models.Custom.TableModels").WithTrailingTrivia(LineFeed))

А нужно было такой:

NamespaceDeclaration(
    QualifiedName(
        QualifiedName(
            QualifiedName(
                IdentifierName("GSCrm"),
                Token(DotToken),
                IdentifierName("Models")),
            Token(DotToken),
            IdentifierName("Custom")),
        Token(DotToken),
        IdentifierName("TableModels")))
    .WithNamespaceKeyword(Token(NamespaceKeyword).WithTrailingTrivia(Whitespace(" ")))
→ Ссылка