CSharpCodeProvider說是支持.net core 經過實驗是不支持的,所以動態編譯選擇roslyn。
首先,我們需要建立一個項目 .net core ,nuget 安裝Microsoft.CodeAnalysis.CSharp和 Microsoft.CodeAnalysis.CSharp.Workspaces.
哪我就直接上代碼 就不多說 說一些遇到問題點
public static bool BuildAssembly(string TenancyFID, string codeStr, string assemblyName, string AppBasePath = null, string[] AssemblyName=null)
{
if (string.IsNullOrWhiteSpace(TenancyFID) || string.IsNullOrWhiteSpace(codeStr) || string.IsNullOrWhiteSpace(assemblyName))
{
return false;
}
if(AppBasePath==null)
{
AppBasePath = SystemParas.AppBasePath;
}
String path = AppBasePath + "/wwwroot/Tenancy/" + TenancyFID + "/DLL/";
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(codeStr);
try
{
string fileFullName = path + assemblyName + ".cs";// "生成cs文件的保存位置";
string dllFullName = path + assemblyName + ".dll";
if (Directory.Exists(path) == false)//如果不存
{
Directory.CreateDirectory(path);
}
if (File.Exists(fileFullName))
{
File.Delete(fileFullName);
}
File.WriteAllText(fileFullName, codeStr);
string[] refPaths=null;
if (AssemblyName != null && AssemblyName.Length > 0)
{
}
else
{
string basePath = Environment.CurrentDirectory + "\\refs";
if (Directory.Exists(basePath))//如果不存
{
DirectoryInfo root = new DirectoryInfo(basePath);
FileInfo[] files = root.GetFiles("*.dll");
refPaths = files.Select(x => x.FullName).ToArray();
}
else
{
refPaths = new string[0];
}
}
//相關的依賴dll加載,由於網上幾個依賴的會缺少,我將所有的放到一個文件夾下面,再加載,當其他項目引用的時候就不會報錯誤。
MetadataReference[] references = refPaths.Select(r => MetadataReference.CreateFromFile(r)).ToArray();
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: GetCompilationOption());
//EmitResult result = compilation.Emit(dllFullName);
using (MemoryStream dllStream = new MemoryStream())
using (MemoryStream pdbStream = new MemoryStream())
//通過這個win32的資源流來給動態編譯的dll產生版本相關的文件信息【問題點,尋找很久才找到】
using (Stream win32resStream = compilation.CreateDefaultWin32Resources(
versionResource: true, // Important!
noManifest: false,
manifestContents: null,
iconInIcoFormat: null))
{
var result = compilation.Emit(
peStream: dllStream,
pdbStream: pdbStream,
win32Resources: win32resStream);
String outputMessage = "";
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
outputMessage += string.Format("\t{0}: {1}", diagnostic.Id, diagnostic.GetMessage()) + Environment.NewLine;//調試的最終輸出信息
Logger.log.Warn("MrCompilation.AssemblyManager.BuildAssembly error:" + Environment.NewLine + outputMessage);
}
return false;
}
else
{
//ms.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes(dllFullName, dllStream.ToArray());
}
}
}
catch (Exception ex)
{
Logger.log.Error("MrCompilation.AssemblyManager.BuildAssembly", ex);
return false;
}
return true;
}
依賴的dll 可以在vs 編譯生產的 查看其應用的dll 再自己從目錄複製出來如圖所示
總結 一下 動態編譯 出現問題,
1、編譯成功,但是在引用的時候報缺少 一個dll,發現編譯的默認繼承 system.object類,而我們正常建的cs和編譯出來的dll 是沒有顯示繼承的。由於直接顯示繼承 就缺少哪個 dll。後來經過監控vs編譯的過程 發現vs編譯會從
2、問題生產的dll 沒有版本號和相關信息
cs文件頭部加入版本信息之後,在通過這樣編譯產生的文件就會有版本信息
using (Stream win32resStream = compilation.CreateDefaultWin32Resources(
versionResource: true, // Important!
noManifest: false,
manifestContents: null,
iconInIcoFormat: null))
{
var result = compilation.Emit(
peStream: dllStream,
pdbStream: pdbStream,
win32Resources: win32resStream);
3、考慮到多個cs文件,最好還是用一個工程來管理和編譯
public static bool CreateProjectBuildAssembly(string TenancyFID,Dictionary<string,string> dicSourceContent,string projectName, out string ErrorMessage, string AppBasePath = null, string[] AssemblyName = null)
{
bool buildResult = true;
ErrorMessage = null;
List<string> refPaths=null;
if (AppBasePath == null || string.IsNullOrWhiteSpace(AppBasePath))
{
string refPath = SystemParas.AppBasePath + "\\refs";
if (Directory.Exists(refPath))//如果不存
{
DirectoryInfo root = new DirectoryInfo(refPath);
FileInfo[] files = root.GetFiles("*.dll");
refPaths = files.Select(x => x.FullName).ToList();
}
}
else
{
if (Directory.Exists(AppBasePath))//如果不存
{
DirectoryInfo root = new DirectoryInfo(AppBasePath);
FileInfo[] files = root.GetFiles("*.dll");
if (AssemblyName != null && AssemblyName.Length > 0)
{
refPaths = files.Where(m => AssemblyName.Contains(m.FullName)).Select(x => x.FullName).ToList();
}
else
{
refPaths = files.Select(x => x.FullName).ToList();
}
}
}
if(refPaths==null|| refPaths.Count<1)
{
return false;
}
string mrEntityDllPath = Assembly.GetExecutingAssembly().Location.Replace("MrCompilation.dll", "MrEntity.dll");
if (!refPaths.Contains(mrEntityDllPath))
{
refPaths.Add(mrEntityDllPath);
}
if(dicSourceContent.Keys.Count()<1)
{
return false;
}
MetadataReference[] references = refPaths.Select(r => MetadataReference.CreateFromFile(r)).ToArray();
var adhoc = new AdhocWorkspace();
var solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Default);
adhoc.AddSolution(solutionInfo);
var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Default, projectName, projectName, "C#")
.WithMetadataReferences(references)
.WithCompilationOptions(GetCompilationOption());
adhoc.AddProject(projectInfo);
var basePathCS = SystemParas.AppBasePath + "/wwwroot/Tenancy/" + TenancyFID + "/CS/";
foreach (var className in dicSourceContent.Keys)
{
var filePath = basePathCS + className + ".cs";
if (Directory.Exists(basePathCS) == false)//如果不存
{
Directory.CreateDirectory(basePathCS);
}
System.IO.File.WriteAllText(filePath, dicSourceContent[className]);
//SourceText sourceText = SourceText.From(sigleSource);
adhoc.AddDocument(projectInfo.Id, className+".cs", SourceText.From(dicSourceContent[className]));
}
var version1 = DateTime.UtcNow.Date.Subtract(DateTime.Parse("2000-01-01")).TotalDays.ToString("f0");
var version2 = DateTime.UtcNow.TimeOfDay.TotalSeconds.ToString("f0");
var versionStr = string.Format("1.0.{0}.{1}", version1, version2);
var versionInfo = AddVersionInfo(projectName, versionStr, versionStr, projectName, versionStr, "MR@Copyright");
adhoc.AddDocument(projectInfo.Id, "AssemblyInfo.cs", SourceText.From(versionInfo));
var project = adhoc.CurrentSolution.GetProject(projectInfo.Id);
var compilation = project.GetCompilationAsync().Result;
using (MemoryStream dllStream = new MemoryStream())
using (Stream win32resStream = compilation.CreateDefaultWin32Resources(
versionResource: true, // Important!
noManifest: false,
manifestContents: null,
iconInIcoFormat: null))
{
EmitResult result = compilation.Emit(dllStream, win32Resources: win32resStream);
if (!result.Success)
{
buildResult = false;
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
StringBuilder sboutputMessage = new StringBuilder();
foreach (Diagnostic diagnostic in failures)
{
sboutputMessage.AppendFormat("\t{0} {1}: {2}", diagnostic.Location.ToString(), diagnostic.Id, diagnostic.GetMessage()+ Environment.NewLine) ;//調試的最終輸出信息
}
ErrorMessage = sboutputMessage.ToString();
Logger.log.Warn("MrCompilation.AssemblyManager.CreateProjectBuildAssembly error:" + Environment.NewLine + ErrorMessage);
}
else
{
var basePath = SystemParas.AppBasePath + "/wwwroot/Tenancy/" +TenancyFID+"/DLL/";
var filePath= basePath+ projectName + ".dll";
if (Directory.Exists(basePath) == false)//如果不存
{
Directory.CreateDirectory(basePath);
}
System.IO.File.WriteAllBytes(filePath, dllStream.ToArray());
}
}
return buildResult;
}