基于 Roslyn 實現代碼動態編譯

基于 Roslyn 實現代碼動態編譯

Intro

之前做的一個數據庫小工具可以支持根據 Model 代碼文件生成創建表的 sql 語句,原來是基于 CodeDom 實現的,后來改成使用基于 Roslyn 去做了。

實現的原理在于編譯選擇的Model 文件生成一個程序集,再從這個程序集中拿到 Model (數據庫表)信息以及屬性信息(數據庫表字段信息),拿到數據庫表以及表字段信息之后就根據數據庫類型生成大致的創建表的 sql 語句。

最近做的一個小工具 dotnet-exec 也是類似的,將代碼編譯成一個程序集并通過反射的方式執行代碼邏輯,

分享一下用到的一些代碼

Sample

來看一個最簡單的編譯一段文本為程序集示例:

//?分析語法樹
var?syntaxTree?=?CSharpSyntaxTree.ParseText(sourceText,?new?CSharpParseOptions(LanguageVersion.Latest));//?配置引用
var?references?=?new[]
{typeof(object).Assembly,Assembly.Load("netstandard"),Assembly.Load("System.Runtime"),
}
.Select(assembly?=>?assembly.Location).Distinct().Select(l?=>?MetadataReference.CreateFromFile(l)).Cast<MetadataReference>().ToArray();var?assemblyName?=?$"DbTool.DynamicGenerated.{GuidIdGenerator.Instance.NewId()}";
//?獲取編譯
var?compilation?=?CSharpCompilation.Create(assemblyName).WithOptions(new?CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).AddReferences(references).AddSyntaxTrees(syntaxTree);using?var?ms?=?new?MemoryStream();
//?生成編譯結果并導出程序集信息到?stream?中
var?compilationResult?=?compilation.Emit(ms);
if?(compilationResult.Success)
{var?assemblyBytes?=?ms.ToArray();//?加載程序集return?Assembly.Load(assemblyBytes);
}var?error?=?new?StringBuilder();
foreach?(var?t?in?compilationResult.Diagnostics)
{error.AppendLine($"{t.GetMessage()}");
}
throw?new?ArgumentException($"Compile?error:{Environment.NewLine}{error}");

多段文本的編譯示例:

var?parseOptions?=?new?CSharpParseOptions(LanguageVersion.Latest);
var?syntaxTrees?=?sourceText.Select(text?=>?CSharpSyntaxTree.ParseText(text)).ToArray();
var?references?=?new[]
{typeof(object).Assembly,Assembly.Load("netstandard"),Assembly.Load("System.Runtime"),
}
.Select(assembly?=>?assembly.Location).Distinct().Select(l?=>?MetadataReference.CreateFromFile(l)).Cast<MetadataReference>().ToArray();
var?assemblyName?=?$"DbTool.DynamicGenerated.{GuidIdGenerator.Instance.NewId()}";
var?compilationOptions?=?new?CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var?compilation?=?CSharpCompilation.Create(assemblyName,?syntaxTrees,?references,?compilationOptions);await?using?var?ms?=?new?MemoryStream();
var?compilationResult?=?compilation.Emit(ms);
if?(compilationResult.Success)
{var?assemblyBytes?=?ms.ToArray();return?Assembly.Load(assemblyBytes);
}var?error?=?new?StringBuilder();
foreach?(var?t?in?compilationResult.Diagnostics)
{var?msg?=?CSharpDiagnosticFormatter.Instance.Format(t);error.AppendLine($"{msg}");
}
throw?new?ArgumentException($"Compile?error:{error}");

之前的做法是合并成一段文本,并將多段代碼的 using 引用合并,可以參考下面的將多個文件代碼合并成一段文本,后來發現自己傻了,改成了上面的用法,直接生成多個語法樹再生成編譯,推薦使用上面的方式,會更加的友好和

var?usingList?=?new?List<string>();var?sourceCodeTextBuilder?=?new?StringBuilder();
foreach?(var?path?in?sourceFilePaths.Distinct())
{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}";

More

如果需要指定 C# 代碼版本可以通過CSharpParseOptions 來指定,比如要使用 preview 特性可以使用 new CSharpParseOptions(LanguageVersion.Preview)

默認地編譯會編譯成一個 dll 程序集,如果包含 Main 方法要生成一個可執行程序可以通過指定 CSharpCompilationOptionsOutputKindOutputKind.ConsoleApplication, 還有很多可以配置的選項,有需要可以自己探索一下

References

  • https://github.com/WeihanLi/DbTool/blob/packages/src/DbTool.Core/DefaultModelCodeExactor.cs

  • https://github.com/WeihanLi/DbTool.Packages/blob/main/src/DbTool.Core/DefaultCSharpModelCodeExtractor.cs

  • https://mp.weixin.qq.com/s?__biz=Mzg5MDEzNjA3Nw==&mid=2247483821&idx=1&sn=c2f4a672bc9bb1f939cdaaebb26eb8ac&chksm=cfe072cff897fbd9a0de68eec9a45a21d63b943d497723853b8944c5a5c48daede13d16db048&scene=21#wechat_redirect

  • https://github.com/WeihanLi/dotnet-exec/blob/main/src/dotnet-exec/CodeCompiler.cs

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/286482.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/286482.shtml
英文地址,請注明出處:http://en.pswp.cn/news/286482.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【GIS風暴】GIS拓撲關系原理詳解

目 錄 1. 拓撲關系的概念2. 拓撲元素3. 拓撲關系4. 拓撲關系的意義5. 拓撲在ArcGIS中實現1. 拓撲關系的概念 地圖上的拓撲關系是指圖形在保持連續狀態下的變形(縮放、旋轉和拉伸等),但圖形關系不變的性質。 2. 拓撲元素 對二維而言,矢量數據可抽象為點(節點)、線(鏈、…

Android之簡單的文件夾選擇器實現

1、效果爆照 2、代碼實現 前提需要保證app有讀寫權限 activity_select_folder.xml文件如下 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layo…

【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照

目錄 【iVX 初級工程師培訓教程 10篇文拿證】01 了解 iVX 完成新年賀卡 【iVX 初級工程師培訓教程 10篇文拿證】02 數值綁定及自適應網站制作 【iVX 初級工程師培訓教程 10篇文拿證】03 事件及猜數字小游戲 【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照 【iV…

360極速瀏覽器使用postman

步驟如下&#xff1a;1、將crx文件打包成zip文件2、解壓打包的zip文件&#xff0c;并將_metadata文件夾修改為metadata3、打開360瀏覽器的擴展4、360瀏覽器加載postman插件5、創建快捷方式6、雙擊快捷方式打開postman下載地址&#xff1a;http://pan.baidu.com/s/1c1ZX8XE如果網…

centos 下安裝man手冊

安裝centos minimal版本&#xff0c;發現沒有man手冊 需要安裝一下&#xff0c;yum install man-pages 本文轉自 XDATAPLUS 51CTO博客&#xff0c;原文鏈接:http://blog.51cto.com/xdataplus/1796126

# javascript 總結

# javascript 總結 ## 語法1. 區分大小寫2. 命名規范1. 首字母必須是 字母 _ $2. 其他字符可以是 數字 字母 下劃線 $3. 避開系統的關鍵字4. 單詞和單詞連接方式推薦駝峰命名3. 注釋1. 單行注釋 //注釋的內容2. 多行注釋 /*注釋內容*/4. 語句1. 要用;結尾(推薦做法)2. 如果不寫…

聊聊 C++ 和 C# 中的 lambda 玩法

這幾天在看 C 的 lambda 表達式&#xff0c;挺有意思&#xff0c;這個標準是在 C11標準 加進去的&#xff0c;也就是 2011 年&#xff0c;相比 C# 2007 還晚了個 4 年&#xff0c; Lambda 這東西非常好用&#xff0c;會上癮&#xff0c;今天我們簡單聊一聊。一&#xff1a;語法…

Android之網絡請求通過協程+okhttp的沒有做網絡異常處理導致程序奔潰問題

1 問題 app里面的網絡請求是通過協程+okhttp來實現的,但是沒有做網絡異常處理(域名無法解析、502錯誤等等一系列),導致程序奔潰 2 嘗試 因為app基本上做好了,外面有大幾十個地方調用,然后又有不同的作用域,調用的地方太多了,一開始修改在最外出的網絡請求地方直接加上…

Windows10系統重裝后必不可少的優化步驟

1. 查看系統的激活狀態 Win+R,打開運行,輸入slmgr.vbs -xpr,回車! 可以看到,該系統沒有永久激活,即將過期,過期后部分功能會不可使用,需要重新激活。 2. 徹底關掉Windows Defender 方法一: 打開“命令提示符(管理員)”,然后輸入: reg add "HKEY_LOCAL_MA…

【iVX 初級工程師培訓教程 10篇文拿證】03 事件及猜數字小游戲

目錄 【iVX 初級工程師培訓教程 10篇文拿證】01 了解 iVX 完成新年賀卡 【iVX 初級工程師培訓教程 10篇文拿證】02 數值綁定及自適應網站制作 【iVX 初級工程師培訓教程 10篇文拿證】03 事件及猜數字小游戲 【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照 【iV…

visual studio系列(vs)啟動調試網站使用ip+端口局域網訪問

vs系列工具創建web應用時會自動創建一些配置文件&#xff0c;本文章講的是如何修改配置文件&#xff0c;使vs在啟動運行調試情況下 使用ip端口形式在局域網訪問。本文章使用的是vs2015&#xff0c;.net 4.5。步驟如下&#xff1a;1.使用vs創建好你的web應用&#xff0c;打開項目…

Android studio之編譯提示Could not find :umeng-asms-v1.2.1:.

1 、問題 Could not determine the dependencies of task :app:compileDebugJavaWithJavac. > Could not resolve all task dependencies for configuration :app:debugCompileClasspath.> Could not find :umeng-asms-v1.2.1:.Required by:project :app> Could not …

1-100之間的奇數

#include "stdio.h" int main() {int i0;for(i1;i<100;i){if(i%21){printf("%d ",i);}}return 0; }轉載于:https://blog.51cto.com/zhangxinbei/1718010

計算機與操作系統基礎小結

計算機基礎概念 1946年二月美國&#xff0c;世界上第一臺電子計算機ENIAC誕生&#xff0c;似乎從這一年開始世界便逐漸變得不一樣了。隨著半個世紀的時間&#xff0c;計算機技術蓬勃發展&#xff0c;推動人類進入信息社會。 計算機操作界面&#xff1a; ①圖形用戶界面 ②命令行…

WPF效果第一百八十九篇之又玩Expander+ListBox

上一篇文章已經提前預告了今天要分享的效果,今天接著上一篇的效果接著去實現,還是先來看最終實現的效果:1、關于簡單的布局設計:總結&#xff1a;①③是Expander②④⑤⑥是ListBox2、把上一篇的②這一塊用ListBox替換:<Expander Grid.Column"1" ExpandDirection&q…

作為一個甘肅天水人,我對罐罐茶有一種特殊的情懷

作為一個出生在罐罐茶世家的80后鄉土人&#xff0c;經歷了罐罐茶的發展演變歷史&#xff0c;與罐罐茶結下了不解情緣&#xff0c;下面我就跟大家分享一下劉一哥與罐罐茶的故事吧。 久違的罐罐茶.mp4美麗邂逅 我最早接觸罐罐茶應該是90年代&#xff0c;那個年代家里條件很不好&…

【iVX 初級工程師培訓教程 10篇文拿證】02 數值綁定及自適應網站制作

目錄 【iVX 初級工程師培訓教程 10篇文拿證】01 了解 iVX 完成新年賀卡 【iVX 初級工程師培訓教程 10篇文拿證】02 數值綁定及自適應網站制作 【iVX 初級工程師培訓教程 10篇文拿證】03 事件及猜數字小游戲 【iVX 初級工程師培訓教程 10篇文拿證】04 畫布及我和 iVX 合照 【iV…

將WebApiTestClient添加到ASP.NET Web API幫助頁面

ASP.NET Web API幫助頁面是一種有用的擴展&#xff0c;可為您的Web API自動生成基于Web的文檔。它使調試變得更容易&#xff0c;因為您可以將幫助頁面中的信息復制/粘貼到Fiddler等工具中&#xff0c;以調用Web API服務并檢查響應。現在&#xff0c;如果您可以直接在幫助頁面上…

【LeetCode】3. Longest Substring Without Repeating Characters

題目&#xff1a; Given a string, find the length of the longest substring without repeating characters. Examples: Given "abcabcbb", the answer is "abc", which the length is 3. Given "bbbbb", the answer is "b", with t…

Android之判斷時間戳是不是今天

1 需求 判斷時間戳是不是今天 2、工具代碼 /*** 獲取每日0點時間* @return*/fun getTodayTime(timeStamp: Long): Long {val cal = Calendar.getInstance()cal.timeInMillis = timeStampcal.set(Calendar.HOUR_OF_DAY, 0)cal.set(Calendar.SECOND, 0)cal.set(Calendar.MINUTE,…