基於 Roslyn 實現動態編譯

来源:https://www.cnblogs.com/weihanli/archive/2019/06/29/dynamic-compile-via-roslyn.html
-Advertisement-
Play Games

之前做的一個資料庫小工具可以支持根據 Model 代碼文件生成創建表的 sql 語句,原來是基於 CodeDom 實現的,最近改成使用基於 Roslyn 去做了。實現的原理在於編譯選擇的Model 文件生成一個程式集,再從這個程式集中拿到 Model (資料庫表)信息以及屬性信息(資料庫表欄位信息)... ...


基於 Roslyn 實現動態編譯

Intro

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

CodeFirst 效果如下圖所示:
Code First

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

遷移原因

最初的 CodeDom 也是可以用的,但是有一些比較新的 C# 語法不支持,比如 C#6 中的指定屬性初始值 public int Number {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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、實例方法和自動綁定self 在類中定義的實例方法,Python會自動綁定方法的第一個參數(通常是self,下文也預設為self),第一個參數總會指向調用該方法的對象,因為實例方法(包括構造方法)第一個self參數會自動綁定,所以調用普通方法和實例方法時不需要為第一個參數傳值(方法第一個參數代表的 ...
  • 1.主體函數 2. 相關函數 3.實驗 4.結果 ...
  • m3u8解析、轉碼、下載、合併 現在網也上大多數視頻需要下載都很麻煩,極少數是MP4,大多都是m3u8, 先說視頻下載, pc端: 打開網頁,點擊視頻播放,打開開發者工具,找到網路那一欄, 等整個網頁載入完成後,可以再下圖指示的地方進行搜索,搜mp4.或者m3u8,搜出來之後,滑鼠右鍵複製鏈接, 在 ...
  • 1.運行程式 python 使用cmd.exe 運行程式。 例子: python +【文件路徑】 2.註釋 當行註釋:# 被註釋內容 多行註釋:'''被註釋內容''',或者"""被註釋內容""" 3.縮進 python 中作用域通過縮進控制 頂級代碼必須頂行寫,即如果一行代碼本身不依賴於任何條件,那 ...
  • 命名空間 從python解釋器開始執行之後,就在記憶體中開闢了一個空間,每當遇到一個變數的時候,就把變數名和值之間的對應關係記錄下來。但是當遇到函數定義的時候解釋器只是象徵性的將函數名讀入記憶體,表示知道這個函數的存在了,至於函數內部的變數和邏輯解釋器根本不關心。等執行到函數調用的時候,python解釋 ...
  • Spring框架的主要功能是通過其核心容器來實現的。Spring提供了2種核心容器:BeanFactory、ApplicationContext。 BeanFactory BeanFactory是一個管理Bean的工廠,主要負責初始化各種Bean,並調用它們的聲明周期方法。 BeanFactory由 ...
  • 最近在學習python,分別在Ubuntu和win10環境下安裝了pycharm,最初學習基礎知識是在Ubuntu下進行的。 想試試在win10下練練手,自然也就想到了釋放出一個可執行文件試試。 我參考了百度經驗去做 利用pyinstaller來進行打包,沒有成功。 經過一番查閱資料,找到瞭解決辦法 ...
  • .Net for Spark 實現WordCount應用及調試入坑詳解 1. 概述 iNeuOS雲端操作系統現在具備物聯網、視圖業務建模、機器學習的功能,但是缺少一個計算平臺產品。最近在調研使用什麼語言進行開發,並且研究實現的技術路線。iNeuOS全系使用C#/JS/CSS/PYTHON開發,所以優 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...