快捷搜索:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

本文将通过一个小Demo的实现讲述如何使用Roslyn脚本化代码,以及如何采用脚本化的代码对一个网站接口实现脚本控制Before和After过滤器的功效。

Demo 源码地址:https://github.com/sevenTiny/Demo.CSharp

一、熟悉Roslyn API

按顺序引入下面三个Nuget包

Microsoft.CodeAnalysis.CSharp   

Microsoft.CodeAnalysis.ing 

Microsoft.CodeAnalysis.CSharp.ing

1.我们写一个Run脚本的Demo:

[Fact][Trait("desc", "调用动态创建的脚本方法")]publicvoidCallFromText(){ stringcode1 = @"public class edClass { public string HelloWorld { get; set; } public edClass() { HelloWorld = ""Hello Roslyn!""; } }"; var = CSharp.RunAsync(code1).Result; varresult = .ContinueWithAsync< string>( "new edClass().HelloWorld").Result; Assert.Equal("Hello Roslyn!", result.ReturnValue);}

Demo中,我们用字符串定义了一个类,并在其中写了小段逻辑,通过Run方法和ContinueWityAsync方法分别执行了两段脚本,最终的结果输出了:

"Hello Roslyn!"

2.我们再写一个脚本调用已存在的类的Demo:

首先我们定义一个类型:

publicclassTestClass{ publicstringarg1 { get; set; } publicstringGetString() { return"hello world!"; } publicstringDealString( stringa) { returna; }}

然后写脚本执行该类型里面的DealString方法(带参数和返回值的)

[Trait( "desc", "使用类的实例调用类的带参数的方法,并获取返回值")][Theory][InlineData("123")]publicvoidCallFromAssemblyWithArgument( stringx){ var = CSharp.Create< string>( "return new TestClass().DealString(arg1);", Options.Default .WithReferences(typeof(TestClass).Assembly) .WithImports("Test.Standard.Dynamic"), globalsType: typeof(TestClass)); .Compile(); varresult = .RunAsync( newTestClass { arg1 = x }).Result.ReturnValue; Assert.Equal(x, result.ToString());}

RunAsync 方法传递参数,参数名必须要和参数类型的字段名称一直才可以识别

Options.Default.WithReferences 明确程序集要引用的类型,类似于引用一个dll

Options.Default.WithImports 明确代码中引用的类型,类似于using

globalsType: typeof(TestClass) 指定了传递参数需要用到的类型(API不支持隐式的参数,只能定义一个明确类型传递参数)

.Compile(); 方法将脚本编译并保存到内存中,待调用

.RunAsync(new TestClass { arg1 = x }).Result.ReturnValue 调用脚本并传递参数获取返回值,x=“123”,单元测试传递的参数

然后我们便得到了“123”的返回值

更多的API我们可以从官方介绍文档中轻松得到

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

使用Roslyn脚本化C#代码,C#动态脚本实现方案

二、一个MVC Action Before/After Filter(Action执行前后过滤器)的Demo   首先说明项目背景及功能

Demo的管道形式的数据流如下:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

Demo界面:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

我们从代码中可以看到上述描述的数据流程:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

ss是执行文件保存的Before脚本后的结果

然后我们把他当作校验Name的参数

result是data数据执行After脚本之后的结果,然后我们将最终的结果返回到界面

测试Demo的提供:

usingSystem.Collections.Generic;namespaceDemo.CSharp.Models{ ///<summary>///测试实体 ///</summary>publicclassDemoModel { publicintID { get; set; } publicintAge { get; set; } publicstringName { get; set; } publicstringDesc { get; set; } ///<summary>///测试数据 ///</summary>///<returns></returns>publicstaticList<DemoModel> GetDemoDatas() { varlist = newList<DemoModel> (); for( inti = 0; i < 100; i++ ) { list.Add(newDemoModel { ID = i, Age = i, Name = $ "7tiny_{i}", Desc = $ "第{i}条测试数据"}); } returnlist; } }}

下面我们来看看两处执行脚本的地方

首先是Before处理逻辑:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

拼接了一个脚本(中间部分从文件读取),使用Roslyn API进行动态编译执行,然后将执行的结果返回

然后是After处理逻辑:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

同样是拼接了一个脚本(中间部分从文件读取),使用Roslyn API进行动态编译执行,然后将执行的结果返回

在上述过程中还将多个命名空间引入,以便在After脚本中写Linq语法,否则会执行失败,出现异常

语法我们在上述章节都已经演示过了

实际我们的脚本:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

before中直接忽略了参数返回了字符串“1”,然后我们Action代码首先过滤的数据就剩下Name字段包含“1”的

after中再次使用Where语法,过滤剩下数据中Name字段包含“3”的

那么,我们的结果中只剩下两条符合条件:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

拓展一下

使用Roslyn脚本化C#代码,C#动态脚本实现方案

我们的测试到此本也结束了,但是为了我们测试脚本更加方便,我这里提供了一个微软刚出的工具,try.dot.net

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

我们可以在测试站点上点“点我帮助你写脚本”的菜单:

然后进入try.dot.net的界面:

使用Roslyn脚本化C#代码,C#动态脚本实现方案

在这里我们可以使用智能提示编写脚本,写完后粘贴回测试的页面,避免文本框写代码出现错误

三、开拓视野

我们今天用的是 Roslyn,事实上,微软有很多类库可以帮助我们执行动态脚本代码,例如:

CodeDom(动态生成或编译代码)

Clear(执行java脚本和CSharp代码)  https://microsoft.github.io/Clear/Examples/Examples.html

PhpNet(执行Php代码)

JavaDynamicCompiler(执行Java代码)

IronPython

...

有兴趣可以去搜索拓展一下!谢谢~

您可能还会对下面的文章感兴趣: