.net Core 3.1下的Roslyn 動態編譯

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;
        }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章