使用 Roslyn 编译 .net 核心应用程序时出错

Error when compiling .net core app using Roslyn

我正在生成代码,然后使用 Roslyn 进行编译。它适用于 Framework,但当我尝试对 Core 执行相同操作时,它失败了。

错误是:

The type forwarder for type 'System.Func`2' in assembly 'System.Runtime' causes a cycle

这是失败的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;

namespace CoreTest
{

    public class TestCore
    {
        public void Test()
        {
            var dt = new DataTable();
            dt.Columns.Add("A");

            var numbers = new List<int>();
            var items = numbers.Where(q => q > 5).ToList();
        }
    }

}

这是编译它的代码:

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeDom.Providers.DotNetCompilerPlatform;

namespace CompilerTest
{

    public class BuildCodeCore
    {
        private string _CoreAssemblyFolder = @"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app.1.0\ref\netcoreapp2.1";

        public BuildCodeCore() { }

        public List<string> Files { get; set; } = new List<string>();
        public string OutputFileNameAndPath { get; set; }
        public string ReferencedAssembliesPath { get; set; }

        public void BuildCore()
        {
            string assemblyFolder = _CoreAssemblyFolder;
            string coreAssemblyFileName = "System.Runtime.dll";

            var assemblies = GetAssembliesInFolder(assemblyFolder);
            if (!string.IsNullOrWhiteSpace(ReferencedAssembliesPath))
            {
                assemblies.AddRange(GetAssembliesInFolder(ReferencedAssembliesPath));
            }

            CSharpCodeProvider codeProvider = new CSharpCodeProvider();
            ICodeCompiler icc = codeProvider.CreateCompiler();

            CompilerParameters parameters = new CompilerParameters();
            parameters.CoreAssemblyFileName = coreAssemblyFileName;
            parameters.ReferencedAssemblies.AddRange(assemblies.ToArray());
            parameters.GenerateExecutable = false;
            parameters.OutputAssembly = OutputFileNameAndPath;

            CompilerResults results = icc.CompileAssemblyFromFileBatch(parameters, Files.ToArray());

            if (results.Errors.Count > 0)
            {
                foreach (CompilerError error in results.Errors)
                {
                    Console.WriteLine(error);
                }
            }

        }

        private List<string> GetAssembliesInFolder(string assemblyPath)
        {
            var files = Directory.GetFiles(assemblyPath, "*.dll");

            return files.ToList();
        }

    }
}

我指向核心 dll 的 NuGetFallbackFolder 文件夹和来自 nuget 的其他文件夹,System.Data.DataSetExtensions.dllSystem.Data.SqlClient.dll 是 Visual Studio 中工作项目的文件夹。

如果我注释掉 DataTable 的两个行和 using System.Data,它就可以工作。 如果我注释掉数字和项目行,它会起作用。

我在这里错过了什么?

事实证明,CompileAssemblyFromFileBatch 不适用于核心应用程序。

我不得不切换到使用 CSharpCompilation 和 Emit 方法。这适用于编译框架和核心。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace CompilerTest
{
    public class BuildEmit
    {
        private string _CoreAssemblyFolder = @"C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app.1.0\ref\netcoreapp2.1";
        private string _FrameworkAssemblyFolder = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2";

        public BuildEmit() { }

        public List<string> Files { get; set; } = new List<string>();
        public string OutputFileNameAndPath { get; set; }
        public string ReferencedAssembliesPath { get; set; }
        public CompilerType Compiler { get; set; }


        public void Build()
        {
            string assemblyFolder = string.Empty;
            string coreAssemblyFileName = string.Empty;
            switch (Compiler)
            {
                case CompilerType.Framework:
                    assemblyFolder = _FrameworkAssemblyFolder;
                    coreAssemblyFileName = "mscorlib.dll";
                    break;
                case CompilerType.Core:
                    assemblyFolder = _CoreAssemblyFolder;
                    coreAssemblyFileName = "System.Runtime.dll";
                    break;
            }
            PortableExecutableReference objectDef = MetadataReference.CreateFromFile(Path.Combine(assemblyFolder, coreAssemblyFileName));

            List<SyntaxTree> syntaxes = new List<SyntaxTree>();
            foreach (string codeFile in Files)
            {
                var code = File.ReadAllText(codeFile);
                var tree = CSharpSyntaxTree.ParseText(code);
                syntaxes.Add(tree);
            }

            var references = new List<PortableExecutableReference>();
            references.Add(objectDef);

            var assemblies = FilterInvalidAssembies(GetAssembliesInFolder(assemblyFolder));
            if (!string.IsNullOrWhiteSpace(ReferencedAssembliesPath))
            {
                assemblies.AddRange(GetAssembliesInFolder(ReferencedAssembliesPath));
            }
            foreach (string item in assemblies)
            {
                var reference = MetadataReference.CreateFromFile(item);
                references.Add(reference);
            }
            var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(OutputFileNameAndPath), syntaxes, references);
            compilation = compilation.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            var emitResult = compilation.Emit(OutputFileNameAndPath);

            if (!emitResult.Success)
            {
                foreach (var diagnostic in emitResult.Diagnostics)
                {
                    Console.WriteLine(diagnostic.ToString());
                }
                File.Delete(OutputFileNameAndPath);
            }
        }

        private void RemoveStringFromList(List<string> items, string contains)
        {
            var item = items.Where(q => q.Contains(contains)).Select(q => q).FirstOrDefault();
            if (item != null)
            {
                items.Remove(item);
            }
        }

        private List<string> FilterInvalidAssembies(List<string> files)
        {
            RemoveStringFromList(files, "System.EnterpriseServices.Wrapper.dll");
            RemoveStringFromList(files, "System.EnterpriseServices.Thunk.dll");
            //RemoveStringFromList(files, "mscorlib.dll");
            return files;
        }

        private List<string> GetAssembliesInFolder(string assemblyPath)
        {
            var files = Directory.GetFiles(assemblyPath, "*.dll");

            return files.ToList();
        }

    }
}