基于 Roslyn 实现一个简单的条件解析引擎
基于 Roslyn 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的條件解析引擎
Intro
最近在做一個(gè)勛章的服務(wù),我們想定義一些勛章的獲取條件,滿(mǎn)足條件之后就給用戶(hù)頒發(fā)一個(gè)勛章,定義條件的時(shí)候會(huì)定義需要哪些參數(shù),參數(shù)的類(lèi)型,獲取勛章的時(shí)候會(huì)提供所需要的參數(shù),有一些內(nèi)置的參數(shù),內(nèi)置的參數(shù)解析器(ParamResolver)。
最后基于 Roslyn 的 Script + 動(dòng)態(tài)編譯功能實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的條件解析引擎。
Condition Eval Demo
條件解析示例:
[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<string, object>(){{ "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<int>"}});var params1 = new{list = new List<int>(){1,2,3,4,5}};Assert.False(await ScriptEngine.EvalAsync(condition, variables, params1));var params2 = new{list = new List<int>(){1,2,3,4,5,10,12}};Assert.True(await ScriptEngine.EvalAsync(condition, variables, params2)); }實(shí)現(xiàn)原理
實(shí)現(xiàn)的方式是基于 Roslyn 實(shí)現(xiàn)的,核心實(shí)現(xiàn)是基于 Roslyn 的 Script 實(shí)現(xiàn)的,但是 Roslyn Script 的執(zhí)行有一些限制,不支持匿名類(lèi)對(duì)象的解析,因此還基于 Roslyn 運(yùn)行時(shí)根據(jù)變量信息來(lái)動(dòng)態(tài)生成一個(gè)類(lèi)型用于執(zhí)行腳本解析
var result = await CSharpScript.EvaluateAsync<bool>("1 > 2");運(yùn)行時(shí)動(dòng)態(tài)生成代碼在之前的 DbTool 項(xiàng)目中介紹過(guò),介紹文章 基于 Roslyn 實(shí)現(xiàn)動(dòng)態(tài)編譯
詳細(xì)實(shí)現(xiàn)細(xì)節(jié)可以參考代碼 https://github.com/WeihanLi/SamplesInPractice/tree/master/ScriptEngine
Memo
程序集加載在 framework 和 core 環(huán)境下的差異
實(shí)現(xiàn)的時(shí)候我們的項(xiàng)目有 dotnetcore 的,還有 netframework 的,這兩者加載 dll 的時(shí)候略有不同,實(shí)現(xiàn)的時(shí)候用了一個(gè)條件編譯,在 dotnet core 環(huán)境下和 dotnet framework 分開(kāi)處理,在 dotnetcore 中使用 AssemblyLoadContext 來(lái)加載程序集
#if NETCOREAPPvar assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); #elsevar assembly = Assembly.LoadFile(dllPath); #endif程序集要保存到文件
原本打算動(dòng)態(tài)生成的程序集保存的一個(gè) Stream 不保存文件,但是實(shí)際測(cè)試下來(lái)必須要保存到文件才可以,所以在項(xiàng)目根目錄下創(chuàng)建了一個(gè)臨時(shí)目錄 temp 用來(lái)保存動(dòng)態(tài)生成的程序集
Roslyn 動(dòng)態(tài)生成的程序集管理
目前還是比較簡(jiǎn)單的放在一個(gè) temp 目錄下了,總覺(jué)得每一個(gè)類(lèi)型生成一個(gè)程序集有些浪費(fèi),但是好像也沒(méi)辦法修改已有程序集,還沒(méi)找到比較好的解決方案,如果有好的處理方式,歡迎一起交流
More
Natasha 是一個(gè)基于 Roslyn 來(lái)實(shí)現(xiàn)動(dòng)態(tài)編譯,能夠讓你更方便進(jìn)行動(dòng)態(tài)操作,有動(dòng)態(tài)編譯相關(guān)需求的可以關(guān)注一下這個(gè)項(xiàng)目,后面也想用 Natasha 來(lái)優(yōu)化前面提到的問(wèn)題
基于roslyn的動(dòng)態(tài)編譯庫(kù),為您提供高效率、高性能、可追蹤的動(dòng)態(tài)構(gòu)建方案,兼容stanadard2.0, 只需原生C#語(yǔ)法不用Emit。讓您的動(dòng)態(tài)方法更加容易編寫(xiě)、跟蹤、維護(hù)
Reference
https://github.com/WeihanLi/SamplesInPractice/tree/master/ScriptEngine
https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples
https://github.com/dotnetcore/Natasha
總結(jié)
以上是生活随笔為你收集整理的基于 Roslyn 实现一个简单的条件解析引擎的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Angular SPA基于Ocelot
- 下一篇: Abp vNext发布v2.3!