日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于 Roslyn 实现动态编译

發布時間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于 Roslyn 实现动态编译 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于 Roslyn 實現動態編譯

Intro

之前做的一個數據庫小工具可以支持根據 Model 代碼文件生成創建表的 sql 語句,原來是基于 CodeDom 實現的,最近改成使用基于 Roslyn 去做了。實現的原理在于編譯選擇的Model 文件生成一個程序集,再從這個程序集中拿到 Model (數據庫表)信息以及屬性信息(數據庫表字段信息),拿到數據庫表以及表字段信息之后就根據數據庫類型生成大致的創建表的 sql 語句。

CodeFirst 效果如下圖所示:

如果你還不知道這個數據庫小工具,歡迎訪問這個項目了解更多https://github.com/WeihanLi/DbTool

遷移原因

最初的 CodeDom 也是可以用的,但是有一些比較新的 C# 語法不支持,比如 C#6 中的指定屬性初始值 publicintNumber{get;set;}=1;,最初我是遷移到了 Microsoft.CodeDom.Providers.DotNetCompilerPlatform這個是一個 CodeDom 過渡到 Roslyn 的實現,他提供了和 CodeDom 差不多的語法,支持 C#6 的語法。但是還是有個問題,我的項目使用了新的項目文件格式,在 VS 中可以編譯通過,但是 dotnet cli 編譯不通過,詳見 issue https://github.com/aspnet/RoslynCodeDomProvider/issues/51

這個問題已經過去一年了仍未解決,最終決定遷移到 Roslyn,直接使用 Roslyn 實現動態編譯。

對 CodeDom 感興趣的童鞋可以看 DbTool 之前的 commit 記錄,在此不多敘述。

使用 Roslyn 實現動態編譯

Roslyn 好像沒有直接根據幾個文件去編譯(可能有只是我沒發現),我就使用了一個比較笨的辦法,把幾個文件的內容都讀出來,合并在一起(命名空間需要去重),然后去編譯,完整源代碼地址,實現代碼如下:

/// <summary> /// 從 源代碼 中獲取表信息 /// </summary> /// <param name="sourceFilePaths">sourceCodeFiles</param> /// <returns></returns> public static List<TableEntity> GeTableEntityFromSourceCode(params string[] sourceFilePaths) { if (sourceFilePaths == null || sourceFilePaths.Length <= 0) { return null; } var usingList = new List<string>(); var sourceCodeTextBuilder = new StringBuilder(); foreach (var path in sourceFilePaths) { foreach (var line in File.ReadAllLines(path)) { if (line.StartsWith("using ") && line.EndsWith(";")) { // usingList.AddIfNotContains(line); } else { sourceCodeTextBuilder.AppendLine(line); } } } var sourceCodeText = $"{usingList.StringJoin(Environment.NewLine)}{Environment.NewLine}{sourceCodeTextBuilder}"; // 獲取完整的代碼 var systemReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); var annotationReference = MetadataReference.CreateFromFile(typeof(TableAttribute).Assembly.Location); var weihanliCommonReference = MetadataReference.CreateFromFile(typeof(IDependencyResolver).Assembly.Location); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCodeText, new CSharpParseOptions(LanguageVersion.Latest)); // 獲取代碼分析得到的語法樹 var assemblyName = $"DbTool.DynamicGenerated.{ObjectIdGenerator.Instance.NewId()}"; // 創建編譯任務 var compilation = CSharpCompilation.Create(assemblyName) //指定程序集名稱 .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))//輸出為 dll 程序集 .AddReferences(systemReference, annotationReference, weihanliCommonReference) //添加程序集引用 .AddSyntaxTrees(syntaxTree) // 添加上面代碼分析得到的語法樹 ; var assemblyPath = ApplicationHelper.MapPath($"{assemblyName}.dll"); var compilationResult = compilation.Emit(assemblyPath); // 執行編譯任務,并輸出編譯后的程序集 if (compilationResult.Success) { // 編譯成功,獲取編譯后的程序集并從中獲取數據庫表信息以及字段信息 try { byte[] assemblyBytes; using (var fs = File.OpenRead(assemblyPath)) { assemblyBytes = fs.ToByteArray(); } return GeTableEntityFromAssembly(Assembly.Load(assemblyBytes)); } finally { File.Delete(assemblyPath); // 清理資源 } } var error = new StringBuilder(compilationResult.Diagnostics.Length * 1024); foreach (var t in compilationResult.Diagnostics) { error.AppendLine($"{t.GetMessage()}"); } // 獲取編譯錯誤 throw new ArgumentException($"所選文件編譯有錯誤{Environment.NewLine}{error}"); }

Reference

  • https://github.com/WeihanLi/DbTool/blob/wfdev/src/DbTool/Utils.cs#L27

  • https://github.com/WeihanLi/DbTool

  • https://msdn.microsoft.com/en-us/magazine/mt808499.aspx


總結

以上是生活随笔為你收集整理的基于 Roslyn 实现动态编译的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。