1. 程式人生 > >基於 Roslyn 實現一個簡單的條件解析引擎

基於 Roslyn 實現一個簡單的條件解析引擎

# 基於 Roslyn 實現一個簡單的條件解析引擎 ## Intro 最近在做一個勳章的服務,我們想定義一些勳章的獲取條件,滿足條件之後就給使用者頒發一個勳章,定義條件的時候會定義需要哪些引數,引數的型別,獲取勳章的時候會提供鎖需要的引數,有一些內建的引數,內建的引數解析器(ParamResolver)。 最後基於 Roslyn 的 Script + 動態編譯功能實現了一個簡單的條件解析引擎。 ## Condition Eval Demo 條件解析示例: ``` csharp [Fact] public async Task EvalTest() { var condition = "x+y > 10"; var variables = JsonConvert.SerializeObject(new[] { new { Name = "x", Type = "int" }, new { Name = "y", Type = "int" }, }); var params1 = new Dictionary() { { "x", 2 }, { "y", 3 } }; Assert.False(await ScriptEngine.EvalAsync(condition, variables, params1)); var params1_1 = JsonConvert.SerializeObject(params1); Assert.False(await ScriptEngine.EvalAsync(condition, variables, params1_1)); var params2 = new { x = 6, y = 5 }; Assert.True(await ScriptEngine.EvalAsync(condition, variables, params2)); } [Fact] public async Task EvalStringTest() { var condition = "x > y.Length"; var variables = JsonConvert.SerializeObject(new[] { new { Name = "x", Type = "int" }, new { Name = "y", Type = "string" }, }); var params1 = new { x = 1, y = "3" }; Assert.False(await ScriptEngine.EvalAsync(condition, variables, params1)); var params2 = new { x = 6, y = "5211" }; Assert.True(await ScriptEngine.EvalAsync(condition, variables, params2)); } [Fact] public async Task EvalLinqTest() { var condition = "list.Any(x=>x>10)"; var variables = JsonConvert.SerializeObject(new[] { new { Name = "list", Type = "List" } }); var params1 = new { list = new List() { 1,2,3,4,5 } }; Assert.False(await ScriptEngine.EvalAsync(condition, variables, params1)); var params2 = new { list = new List() { 1,2,3,4,5,10,12 } }; Assert.True(await ScriptEngine.EvalAsync(condition, variables, params2)); } ``` ## 實現原理 實現的方式是基於 Roslyn 實現的,核心實現是基於 Roslyn 的 Script 實現的,但是 Roslyn Script 的執行有一些限制,不支援匿名類物件的解析,因此還基於 Roslyn 執行時根據變數資訊來動態生成一個型別用於執行指令碼解析 ``` csharp var result = await CSharpScript.EvaluateAsync("1 > 2"); ``` 執行時動態生成程式碼在之前的 DbTool 專案中介紹過,介紹文章 [基於 Roslyn 實現動態編譯](https://www.cnblogs.com/weihanli/p/dynamic-compile-via-roslyn.html) 詳細實現細節可以參考程式碼
## Memo ### 程式集載入在 framework 和 core 環境下的差異 實現的時候我們的專案有 dotnetcore 的,還有 netframework 的,這兩者載入 dll 的時候略有不同,實現的時候用了一個條件編譯,在 dotnet core 環境下和 dotnet framework 分開處理,在 dotnetcore 中使用 `AssemblyLoadContext` 來載入程式集 ``` csharp #if NETCOREAPP var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); #else var assembly = Assembly.LoadFile(dllPath); #endif ``` ### 程式集要儲存到檔案 原本打算動態生成的程式集儲存的一個 Stream 不儲存檔案,但是實際測試下來必須要儲存到檔案才可以,所以在專案根目錄下建立了一個臨時目錄 `temp` 用來儲存動態生成的程式集 ### Roslyn 動態生成的程式集管理 目前還是比較簡單的放在一個 `temp` 目錄下了,總覺得每一個型別生成一個程式集有些浪費,但是好像也沒辦法修改已有程式集,還沒找到比較好的解決方案,如果有好的處理方式,歡迎一起交流 ## More [Natasha](https://github.com/dotnetcore/Natasha) 是一個基於 Roslyn 來實現動態編譯,能夠讓你更方便進行動態操作,有動態編譯相關需求的可以關注一下這個專案,後面也想用 Natasha 來優化前面提到的問題 >
基於roslyn的動態編譯庫,為您提供高效率、高效能、可追蹤的動態構建方案,相容stanadard2.0, 只需原生C#語法不用Emit。 讓您的動態方法更加容易編寫、跟蹤、維護 ## Reference - - -