Компиляция и выполнение кода во время выполнения in.Net ядро 1.0
можно ли компилировать и запускать код c# во время выполнения в новом .Net Core (лучшая платформа .Net Standard)? Я видел некоторые примеры (.Net Framework), но используемые пакеты NuGet есть
не совместим с netcoreapp1.0 (.NETCoreApp, версия=v1.0)
2 ответов
#1: используйте полный компилятор C# для компиляции сборки, загрузите ее, а затем выполните из нее метод.
для этого требуются следующие пакеты в качестве зависимостей в вашем проекте.в JSON:
"Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160429-01",
"System.Runtime.Loader": "4.0.0-rc2-24027",
затем вы можете использовать такой код:
var compilation = CSharpCompilation.Create("a")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
@"
using System;
public static class C
{
public static void M()
{
Console.WriteLine(""Hello Roslyn."");
}
}"));
var fileName = "a.dll";
compilation.Emit(fileName);
var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
a.GetType("C").GetMethod("M").Invoke(null, null);
#2: Используйте Сценарии Roslyn. Это приведет к гораздо более простому коду, но в настоящее время требует дополнительной настройки:
-
Создать NuGet.config, чтобы получить пакеты из ночной ленты Roslyn:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="Roslyn Nightly" value="https://www.myget.org/F/roslyn-nightly/api/v3/index.json" /> </packageSources> </configuration>
-
добавьте следующий пакет в качестве зависимости от project.json (обратите внимание, что это пакет с сегодняшнего дня, вам понадобится другая версия в будущем):
"Microsoft.CodeAnalysis.CSharp.Scripting": "1.3.0-beta1-20160530-01",
вам также необходимо импортировать
dotnet
(устаревший "целевой фреймворк",хотя по-прежнему используется Рослин):"frameworks": { "netcoreapp1.0": { "imports": "dotnet5.6" } }
-
теперь вы можете, наконец, использовать Сценарии:
CSharpScript.EvaluateAsync(@"using System;Console.WriteLine(""Hello Roslyn."");").Wait();
просто добавив в @ svick вариант один ответ. Если вы хотите сохранить сборку в памяти (а не записывать в файл), вы можете использовать следующий метод:
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
это отличается от того, что в Net451, где код:
Assembly assembly = Assembly.Load(ms.ToArray());
мой код нацелен как на Net451, так и на Netstandard, поэтому мне пришлось использовать директивы, чтобы обойти эту проблему. Полный пример кода здесь:
string code = CreateFunctionCode();
var syntaxTree = CSharpSyntaxTree.ParseText(code);
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Hashtable).GetTypeInfo().Assembly.Location)
};
var compilation = CSharpCompilation.Create("Function.dll",
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
StringBuilder message = new StringBuilder();
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return new ReturnValue<MethodInfo>(false, "The following compile errors were encountered: " + message.ToString(), null);
}
else
{
ms.Seek(0, SeekOrigin.Begin);
#if NET451
Assembly assembly = Assembly.Load(ms.ToArray());
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
#endif
Type mappingFunction = assembly.GetType("Program");
_functionMethod = mappingFunction.GetMethod("CustomFunction");
_resetMethod = mappingFunction.GetMethod("Reset");
}
}