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