简体   繁体   English

在 NetStandard2.0 项目中使用 Roslyn 编译动态创建的代码

[英]Use Roslyn in NetStandard2.0 project to compile dynamically created code

I'm trying to create a reusable .NET Standard 2.0 library that uses Roslyn to dynamically compile code at runtime to an in-memory assembly.我正在尝试创建一个可重用的 .NET 标准 2.0 库,该库使用 Roslyn 在运行时将代码动态编译为内存中的程序集。 This dynamically created assembly contains classes that derive from a base class that is part of the library.这个动态创建的程序集包含派生自作为库一部分的基础 class 的类。 I instantiate them via reflection in the apps that reference the library.我通过引用库的应用程序中的反射来实例化它们。 The project structure looks like this:项目结构如下所示:

项目结构

Suppose that I have the following type in my netstandard2.0 library:假设我的 netstandard2.0 库中有以下类型:

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

I then create the following unit test in a .NET Core 2.2 project:然后我在 .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);
        }
    }
}

In this test, I first parse a piece of C# code that derives from BaseClass in the netstandard2.0 library.在这个测试中,我首先解析了一段 C# 代码,它来自 netstandard2.0 库中的BaseClass This piece of code additionally references System.Math .这段代码还引用了System.Math I then create a C# compilation object that includes references to the core lib (of .NET Core 2.2) and my library.然后,我创建了一个 C# 编译 object,其中包括对核心库(.NET Core 2.2)和我的库的引用。 This compilation object emits the DLL to a memory stream.此编译 object 将 DLL 发射到 memory ZF7B44CFAFD5C52223D5498196C8A2E If compiling fails, the test will fail with an exception that contains all diagnostics.如果编译失败,则测试将失败,并出现包含所有诊断信息的异常。

This unit test does fail with the following error message:此单元测试确实失败并显示以下错误消息:

(7,31): error CS0012: The type 'Object' is defined in an assembly that is not referenced. (7,31):错误 CS0012:“对象”类型是在未引用的程序集中定义的。 You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.您必须添加对程序集“netstandard,Version=2.0.0.0,Culture=neutral,PublicKeyToken=cc7b13ffcd2ddd51”的引用。

(11,26): error CS0012: The type 'Object' is defined in an assembly that is not referenced. (11,26):错误 CS0012:“对象”类型是在未引用的程序集中定义的。 You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.您必须添加对程序集“netstandard,Version=2.0.0.0,Culture=neutral,PublicKeyToken=cc7b13ffcd2ddd51”的引用。

I have the following questions:我有以下问题:

  • Is this not working because the Roslyn NuGet package is referenced in a .NET Standard 2.0 project and thus always tries to compile to the netstandard2.0 Target Framework Moniker?这是否不起作用,因为 Roslyn NuGet package 在 .NET 标准 2.0 项目中被引用,因此总是尝试编译到 netstandard2.0 目标? I suspect that netstandard2.0 has a different definition of System.Object that forwards to the actual implementation of the target platform.我怀疑 netstandard2.0 对System.Object的定义不同,它转发到目标平台的实际实现。 And this forwarding definition is not referenced in my compilation unit.而且我的编译单元中没有引用这个转发定义。
  • Is there a way to change the target framework?有没有办法改变目标框架? I looked at CSharpCompilationOptions and EmitOptions , but couldn't find anything that let's me change the target framework.我查看了CSharpCompilationOptionsEmitOptions ,但找不到任何可以让我更改目标框架的内容。
  • Do I maybe need to use another Roslyn NuGet package such as Microsoft.Net.Compilers.Toolset ?我是否可能需要使用另一个 Roslyn NuGet package 例如Microsoft.Net.Compilers.Toolset I try to avoid this because actually want to use the default compilers instead of the ones in the NuGet package.我尽量避免这种情况,因为实际上想要使用默认编译器而不是 NuGet package 中的编译器。
  • It's not working because your library, which contains BaseClass , targets .netstandard2.0 (it means that this library references netstandard.dll 2.0 ) and this assumes that your library, which references library with BaseClass , should have a reference to netstandard.dll 2.0 to correctrly resolve all corresponding types.它不起作用,因为您的包含BaseClass的库以.netstandard2.0为目标(这意味着该库引用netstandard.dll 2.0 )并且这假定您的库引用带有BaseClass的库应该引用netstandard.dll 2.0正确解析所有相应的类型。 So you should add reference on them (the netstandard.dll for .net47 or the similar .netstandard.dll for .netcore2.2 ).因此,您应该添加对它们的引用( netstandard.dll用于.net47或类似的.netstandard.dll用于.netcore2.2 )。 (By the way, when you reference .netstandard2.0 from .net47 library you probably should add a couple of additional libraries as reference from path_to_visual_studio\MSBuild\Microsoft\Microsoft.NET.Build.Extensions ) (顺便说一句,当您从.net47库中引用.netstandard2.0时,您可能应该添加几个额外的库作为path_to_visual_studio\MSBuild\Microsoft\Microsoft.NET.Build.Extensions的引用)
  • Roslyn Compilation doesn't know anything about target framework and it should not know anything about it. Roslyn Compilation目标框架一无所知,也不应该对此一无所知。 Compilation works with trees and references (and with some options and metadata of references of course), so you should manually append references that will be required in compilation. Compilation适用于树和引用(当然还有一些选项和引用的元数据),因此您应该手动 append 引用,编译中需要这些引用。 (By the way, if you have a csproj or a sln file you may use MsBuildWorkspace that allow to get a ready compilation from the project or solution file, in the most of cases) (顺便说一句,如果你有一个csprojsln文件,你可以使用MsBuildWorkspace允许从项目或解决方案文件中获取准备好的编译,在大多数情况下)
  • I suggest to create Compilation by hand if you know or can find out all references that will be required in compilation, else try to use Microsoft.CodeAnalysis.Workspaces.MSBuild to analyze .csproj or .sln files and then retrieve Compilation from them.如果您知道或可以找到编译中需要的所有引用,我建议手动创建Compilation ,否则尝试使用Microsoft.CodeAnalysis.Workspaces.MSBuild分析.csproj.sln文件,然后从中检索Compilation Microsoft.Net.Compilers.Toolset just gives to you possibility to compile your project by compilers not are installed on your system, but are contained in this package. Microsoft.Net.Compilers.Toolset只是让您有可能通过未安装在您的系统上但包含在此 package 中的编译器来编译您的项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在.netStandard2.0项目中添加“连接服务” - Add “Connected service” in .netStandard2.0 project 如何在 .NetStandard2.0 项目中定位 Windows? - How to target Windows in .NetStandard2.0 project? 错误消息:Project Android.Common与netstandard2.0不兼容 - Error message:Project Android.Common is not compatible with netstandard2.0 将netstandard2.0库加载到net461项目 - Load in netstandard2.0 libraries to net461 project 无法在 Visual Studio 2019 中调试 .netstandard2.0 项目 - Cannot debug netstandard2.0 project in Visual Studio 2019 ISystemClock 在 netstandard2.0 中不可用 - ISystemClock not available in netstandard2.0 在不访问 ServicesCollection 的情况下使用 HttpClientFactory in.netstandard2.0 库 - Use HttpClientFactory in netstandard2.0 library without access to ServicesCollection .NetStandard2.0中的AsymmetricSecurityBindingElement在哪里? - Where is AsymmetricSecurityBindingElement in .NetStandard2.0? Project MyAzureFunc与netstandard2.0>(.NETStandard,Version = v2.0)不兼容 - Project MyAzureFunc is not compatible with netstandard2.0 > (.NETStandard,Version=v2.0) 无法将.Net Core 2.0项目的项目引用添加到Azure函数项目(netStandard2.0) - Not able to add project reference of .Net Core 2.0 project to Azure functions project(netStandard2.0)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM