歡迎光臨
每天分享高質量文章

使用Roslyn腳本化C#代碼,C#動態腳本實現方案

【前言】

Roslyn 是微軟公司開源的 .NET 編譯器。

編譯器支持 C# 和 Visual Basic 代碼編譯,並提供豐富的代碼分析 API。

Roslyn不僅僅可以直接編譯輸出,難能可貴的就是上述描述中的開放了編譯的API,使得代碼腳本化成為了可能。

關於Roslyn,本文不做過多介紹,因為再介紹的豐滿終究不及官方文件介紹的細膩,各位請移步官方說明地址:https://github.com/dotnet/roslyn/wiki

  

眾所周知,我們實現的Filter往往是寫死的代碼在專案裡面的,一經發佈,便不能隨時改動,有過Paas平臺開發經驗的同僚更能體會到租戶靈活配置個性化需求是一個難點。

那麼,我們怎麼能針對不同的業務邏輯靈活地在已經部署好地站點上制定不同地業務邏輯呢,讓我們一起走進這個世界。

本文將通過一個小Demo的實現講述如何使用Roslyn腳本化代碼,以及如何採用腳本化的代碼對一個網站接口實現腳本控制Before和After過濾器的功效。

  Demo 原始碼地址:https://github.com/sevenTiny/Demo.CSharpScript

一、熟悉Roslyn API

  • 首先專案中引入微軟腳本API相關的Nuget包

按順序引入下麵三個Nuget包

Microsoft.CodeAnalysis.CSharp

Microsoft.CodeAnalysis.Scripting

Microsoft.CodeAnalysis.CSharp.Scripting

  • 瞭解API

1.我們寫一個Run腳本的Demo:

[Fact]
[Trait(
desc, 呼叫動態創建的腳本方法)]public void CallScriptFromText()
{    
string code1 = @”
public class ScriptedClass
{
public string HelloWorld { get; set; }
public ScriptedClass()
{
HelloWorld = “”Hello Roslyn!””;
}
}
;    var script = CSharpScript.RunAsync(code1).Result;    var result = script.ContinueWithAsync<string>(new ScriptedClass().HelloWorld).Result;

Assert.Equal(Hello Roslyn!, result.ReturnValue);
}

Demo中,我們用字串定義了一個類,併在其中寫了小段邏輯,通過Run方法和ContinueWityAsync方法分別執行了兩段腳本,最終的結果輸出了:

 ”Hello Roslyn!”

2.我們再寫一個腳本呼叫已存在的類的Demo:

首先我們定義一個型別:

public class TestClass
{    
public string arg1 { get; set; }    public string GetString()
{        
return hello world!;
}    
public string DealString(string a)
{        
return a;
}
}

然後寫腳本執行該型別裡面的DealString方法(帶引數和傳回值的)

[Trait(desc, 使用類的實體呼叫類的帶引數的方法,並獲取傳回值)]
[Theory]
[InlineData(
123)]public void CallScriptFromAssemblyWithArgument(string x)
{    
var script = CSharpScript.Create<string>(return new TestClass().DealString(arg1);,
ScriptOptions.Default
.WithReferences(
typeof(TestClass).Assembly)
.WithImports(
Test.Standard.DynamicScript), globalsType: typeof(TestClass));

script.Compile();    var result = script.RunAsync(new TestClass { arg1 = x }).Result.ReturnValue;

Assert.Equal(x, result.ToString());
}

RunAsync 方法傳遞引數,引數名必須要和引數型別的欄位名稱一直才可以識別

ScriptOptions.Default.WithReferences 明確程式集要取用的型別,類似於取用一個dll

ScriptOptions.Default.WithImports 明確代碼中取用的型別,類似於using

globalsType: typeof(TestClass) 指定了傳遞引數需要用到的型別(API不支持隱式的引數,只能定義一個明確型別傳遞引數)

script.Compile(); 方法將腳本編譯並儲存到記憶體中,待呼叫

script.RunAsync(new TestClass { arg1 = x }).Result.ReturnValue 呼叫腳本並傳遞引數獲取傳回值,x=“123”,單元測試傳遞的引數

然後我們便得到了“123”的傳回值

  • 更多API

更多的API我們可以從官方介紹文件中輕鬆得到

官方WIKI:https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples

  

二、一個MVC Action Before/After Filter(Action執行前後過濾器)的Demo

首先說明專案背景及功能

  1. 運行.netcore mvc站點,點擊選單欄的進入Demo便得到下麵界面
  2. 我們定義了一個Action,按序號創建了100條記錄用於資料演示
  3. before 腳本的name引數是從url獲取到的name引數,傳回結果將作為100條Demo資料的“Name”欄位Contains方法的引數 相當於Linq .Where(t=>t.Name.Contains(name));
  4. after 腳本是將 Where 陳述句過濾後的結果集作為引數,然後執行完腳本中的代碼後,傳回結果展示在了下麵的頁面上
  5. 可以簡單理解為before是校驗url引數的,after是二次處理結果資料的
  6. 為了方便測試,我們的腳本都是從本地檔案讀寫的

Demo的管道形式的資料流如下:

  

Demo界面:

  

  我們從代碼中可以看到上述描述的資料流程:

  

ss是執行檔案儲存的Before腳本後的結果

然後我們把他當作校驗Name的引數

result是data資料執行After腳本之後的結果,然後我們將最終的結果傳回到界面

測試Demo的提供:

using System.Collections.Generic;namespace Demo.CSharpScript.Models
{    
///

/// 測試物體    ///

public class DemoModel
{        
public int ID { get; set; }        public int Age { get; set; }        public string Name { get; set; }        public string Desc { get; set; }        ///

/// 測試資料        ///
///
public static List GetDemoDatas()
{            
var list = new List();            for (int i = 0; i < 100; i++)
{
list.Add(
new DemoModel { ID = i, Age = i, Name = $7tiny_{i}, Desc = $第{i}條測試資料 });
}            
return list;
}
}
}

下麵我們來看看兩處執行腳本的地方

首先是Before處理邏輯:

  

拼接了一個腳本(中間部分從檔案讀取),使用Roslyn API進行動態編譯執行,然後將執行的結果傳回

然後是After處理邏輯:

  

同樣是拼接了一個腳本(中間部分從檔案讀取),使用Roslyn API進行動態編譯執行,然後將執行的結果傳回

在上述過程中還將多個命名空間引入,以便在After腳本中寫Linq語法,否則會執行失敗,出現異常

語法我們在上述章節都已經演示過了

實際我們的腳本:

  

before中直接忽略了引數傳回了字串“1”,然後我們Action代碼首先過濾的資料就剩下Name欄位包含“1”的

after中再次使用Where語法,過濾剩下資料中Name欄位包含“3”的

那麼,我們的結果中只剩下兩條符合條件:

  

拓展一下

  

我們的測試到此本也結束了,但是為了我們測試腳本更加方便,我這裡提供了一個微軟剛出的工具,try.dot.net

不瞭解的同學可以參考之前博文熟悉一下 try.dot.net :https://www.cnblogs.com/7tiny/p/10277600.html

我們可以在測試站點上點“點我幫助你寫腳本”的選單:

  

然後進入try.dot.net的界面:

  

在這裡我們可以使用智慧提示編寫腳本,寫完後粘貼回測試的頁面,避免文本框寫代碼出現錯誤

三、開拓視野

我們今天用的是 Roslyn,事實上,微軟有很多類庫可以幫助我們執行動態腳本代碼,例如:

CodeDom(動態生成或編譯代碼)

ClearScript(執行javascript腳本和CSharp代碼) https://microsoft.github.io/ClearScript/Examples/Examples.html

PhpNet(執行Php代碼)

JavaDynamicCompiler(執行Java代碼)

IronPython

有興趣可以去搜索拓展一下!謝謝~

  最後,本文Demo 原始碼地址:https://github.com/sevenTiny/Demo.CSharpScript

原文地址:https://www.cnblogs.com/7tiny/p/10279349.html

赞(0)

分享創造快樂