使用 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.dll
和 System.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();
}
}
}
我正在生成代码,然后使用 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.dll
和 System.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();
}
}
}