Используйте Roslyn в проекте NetStandard2.0 для компиляции динамически созданного кода - PullRequest
0 голосов
/ 20 сентября 2019

Я пытаюсь создать повторно используемую библиотеку .NET Standard 2.0, которая использует Roslyn для динамической компиляции кода во время выполнения в сборку в памяти.Эта динамически создаваемая сборка содержит классы, которые являются производными от базового класса, который является частью библиотеки.Я создаю их экземпляры через отражение в приложениях, которые ссылаются на библиотеку.Структура проекта выглядит следующим образом:

Project structure

Предположим, у меня есть следующий тип в моей библиотеке netstandard2.0:

namespace MyLibrary
{
    public abstract class BaseClass
    {
        public abstract int CalculateSomething();
    }
}

Затем я создаю следующий модульный тест в проекте .NET Core 2.2:

namespace NetCore2_2.Tests
{
    public static class RoslynTests
    {
        [Fact]
        public static void CompileDynamicallyAndInvoke()
        {
            // Create syntax tree with simple class
            var syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
using MyLibrary;

namespace Foo
{
    public sealed class Bar : BaseClass
    {
        public override int CalculateSomething()
        {
            return (int) Math.Sqrt(42);
        }
    }
}");
            // Create compilation, include syntax tree and reference to core lib
            var compilation = CSharpCompilation.Create(
                "MyDynamicAssembly.dll",
                new[] { syntaxTree },
                new[]
                {
                    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                    MetadataReference.CreateFromFile(typeof(BaseClass).Assembly.Location)
                },
                new CSharpCompilationOptions(
                    OutputKind.DynamicallyLinkedLibrary,
                    optimizationLevel: OptimizationLevel.Release)
            );

            // Compile it to a memory stream
            var memoryStream = new MemoryStream();
            var result = compilation.Emit(memoryStream);

            // If it was not successful, throw an exception to fail the test
            if (!result.Success)
            {
                var stringBuilder = new StringBuilder();
                foreach (var diagnostic in result.Diagnostics)
                {
                    stringBuilder.AppendLine(diagnostic.ToString());
                }

                throw new XunitException(stringBuilder.ToString());
            }

            // Otherwise load the assembly, instantiate the type via reflection and call CalculateSomething
            var dynamicallyCompiledAssembly = Assembly.Load(memoryStream.ToArray());
            var type = dynamicallyCompiledAssembly.GetType("Foo.Bar");
            var instance = (BaseClass) Activator.CreateInstance(type);
            int number = instance.CalculateSomething();
            Assert.Equal((int) Math.Sqrt(42), number);
        }
    }
}

В этом тесте я сначала анализирую фрагмент кода C #, полученный из BaseClass в библиотеке netstandard2.0.Этот фрагмент кода дополнительно ссылается на System.Math.Затем я создаю объект компиляции C #, который включает ссылки на ядро ​​lib (из .NET Core 2.2) и мою библиотеку.Этот объект компиляции испускает DLL в поток памяти.Если компиляция завершится неудачно, тест завершится неудачей с исключением, содержащим всю диагностику.

Этот модульный тест не пройден со следующим сообщением об ошибке:

(7,31): ошибка CS0012: тип«Объект» определяется в сборке, на которую нет ссылок.Необходимо добавить ссылку на сборку 'netstandard, версия = 2.0.0.0, Culture = нейтральная, PublicKeyToken = cc7b13ffcd2ddd51'.

(11,26): ошибка CS0012: тип 'Object' определен в сборкеэто не ссылка.Вы должны добавить ссылку на сборку 'netstandard, версия = 2.0.0.0, Culture = нейтральная, PublicKeyToken = cc7b13ffcd2ddd51'.

У меня есть следующие вопросы:

  • Разве это не работает, потому что на пакет Roslyn NuGet есть ссылка в проекте .NET Standard 2.0, и поэтому он всегда пытается скомпилировать в Netstandard2.0 Target Framework Moniker? Я подозреваю, что netstandard2.0 имеет другойопределение System.Object, которое переходит к фактической реализации целевой платформы.И это определение переадресации не указано в моем модуле компиляции.
  • Есть ли способ изменить целевую платформу? Я посмотрел CSharpCompilationOptions и EmitOptions, но не смог найтивсе, что позволяет мне изменить целевую платформу.
  • Возможно, мне нужно использовать другой пакет Roslyn NuGet, такой как Microsoft.Net.Compilers.Toolset ? Я пытаюсьизбегайте этого, потому что на самом деле хотите использовать компиляторы по умолчанию вместо компиляторов в пакете NuGet.

1 Ответ

1 голос
/ 21 сентября 2019
  • Это не работает, потому что ваша библиотека, которая содержит BaseClass, предназначается .netstandard2.0 (это означает, что эта библиотека ссылается на netstandard.dll 2.0), и это предполагает, что ваша библиотека, которая ссылается на библиотеку с BaseClass, должнаиметь ссылку на netstandard.dll 2.0 для правильного разрешения всех соответствующих типов.Таким образом, вы должны добавить ссылку на них (netstandard.dll для .net47 или аналогичные .netstandard.dll для .netcore2.2).(Кстати, когда вы ссылаетесь на .netstandard2.0 из библиотеки .net47, вы, вероятно, должны добавить пару дополнительных библиотек в качестве ссылки из path_to_visual_studio\MSBuild\Microsoft\Microsoft.NET.Build.Extensions)
  • Рослин Compilation ничего не знает о target framework и он не должен ничего знать об этом.Compilation работает с деревьями и ссылками (и, конечно, с некоторыми опциями и метаданными ссылок), поэтому вы должны вручную добавлять ссылки, которые потребуются при компиляции.(Кстати, если у вас есть файл csproj или sln, вы можете использовать MsBuildWorkspace, который позволяет получить готовую компиляцию из файла проекта или решения, в большинстве случаев)
  • Я предлагаю создать Compilation вручную, если вы знаете или можете узнать все ссылки, которые потребуются при компиляции, иначе попробуйте использовать Microsoft.CodeAnalysis.Workspaces.MSBuild для анализа .csproj или .sln файлы, а затем получить Compilation из них.Microsoft.Net.Compilers.Toolset просто дает вам возможность компилировать ваш проект компиляторами, которые не установлены в вашей системе, но содержатся в этом пакете.
...