来源:https://www.cnblogs.com/7tiny/p/10279349.html

【前言】

  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
{    /// <summary>/// 测试实体    /// </summary>public class DemoModel{        public int ID { get; set; }        public int Age { get; set; }        public string Name { get; set; }        public string Desc { get; set; }        /// <summary>/// 测试数据        /// </summary>/// <returns></returns>public static List<DemoModel> GetDemoDatas(){            var list = new List<DemoModel>();            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


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

点个赞,让我在心里记住你 ☟

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

  1. html5游戏开发-零基础开发RPG游戏-开源讲座(四)-游戏脚本化地图跳转

    首先,本篇文章是零基础开发RPG游戏-开源讲座系列文章的第四篇,来实现游戏的脚本化,和利用游戏脚本实现地图场景的切换,离上次更新貌似很长时间了,你在看下面的文字之前,需要先了解前三篇在下啰嗦了些什么东 ...

  2. Java 如何实现动态脚本?

    简介:在平台级的 Java 系统中,动态脚本技术是不可或缺的一环.本文分享了一种 Java 动态脚本实现方案,给出了其中的关键技术点,并就类重名问题.生命周期.安全问题等做出进一步讨论,欢迎同学们共同 ...

  3. 脚本化HTTP 取得响应 指定请求

    脚本化HTTP 下面将会用js代码操纵HTTP 下面将会说明在没有导致web浏览器重新加载任何窗口或者窗体的情况下,脚本实现web浏览器和服务器之间的通信. ajax:为一种找早起避免页面重载而动态更 ...

  4. 《守望先锋》中网络脚本化的武器和技能系统

    在GDC2017[Networking Scripted Weapons and Abilities in Overwatch]的分享会上,来自暴雪的Dan Reed介绍了<守望先锋>中网 ...

  5. php服务器响应http请求,脚本化HTTP 取得响应 指定请求

    脚本化HTTP 下面将会用js代码操纵HTTP 下面将会说明在没有导致web浏览器重新加载任何窗口或者窗体的情况下,脚本实现web浏览器和服务器之间的通信. ajax:为一种找早起避免页面重载而动态更 ...

  6. ajax将响应结果显示到iframe,脚本化HTTP 取得响应 指定请求

    脚本化HTTP 下面将会用js代码操纵HTTP 下面将会说明在没有致使web浏览器从新加载任何窗口或者窗体的状况下,脚本实现web浏览器和服务器之间的通讯. ajax:为一种找早起避免页面重载而动态更 ...

  7. 第17章 脚本化CSS

    第17章 脚本化CSS CSS脚本化是网页交互效果的技术基础,使用CSS和JavaScript可以设计网页动画.利用脚本化CSS可以动态地改变HTML属性,如字体颜色.字体大小等,还可以用它设置和改变 ...

  8. Java脚本化编程实践整理 ScriptEngineManager万字详解

    文章目录 认识 Java支持脚本语言的意义 Java对JavaScript的支持 Rhino/Nashorn概述 Nashorn的目的 实践操作 HelloWorld 执行脚本文件代码 脚本语言使用J ...

  9. Javascript学习7 - 脚本化浏览器窗口

    原文:Javascript学习7 - 脚本化浏览器窗口 本节讨论了文档对象模型.客户端Javascript下Window中的各项属性,包括计时器.Location对象.Histroy对象.窗口.浏览器 ...

最新文章

  1. opencv源码解析之(6):hog源码分析
  2. SD-WAN开源优势是什么?
  3. vue中Router的封装以及使用
  4. 语言资源的类别、搜索与搭建策略
  5. [导入]如何动态生成table(javascript)
  6. java折行_Java源代码的折行规则
  7. 查找文件夹下图片的数量
  8. MinGW编译windows可以调试的ffmpeg4.4
  9. dell服务器双系统切换,戴尔笔记本双系统在不关机的状况下怎么转换另外一个系统?...
  10. 投资理财之基金篇(一) - 认识基金
  11. python中length用法_为什么Python代码使用len()函数而不是length方法?
  12. 经验分享|裸金属服务器部署
  13. Simulink 界面模型的矢量图复制
  14. 绝地求生+守望先锋?2019首款黑马游戏Apex英雄凭什么如此火爆?
  15. EndPoint详解
  16. linux内核ppt刘小明,【陈老师华为北研所讲座PPT】从机制与策略探究Linux内核设计之道(4)...
  17. Rebus渲染农场分析
  18. 【数学建模】4 马尔萨斯人口论
  19. 《软件功能测试自动化实战教程》—第6章6.4节Action测试输入的参数化
  20. java版 银行还款计算

热门文章

  1. 点击回退按钮刷新页面
  2. js时间戳转成日期格式
  3. 海尔联手软银机器人,进军服务机器人领域
  4. 光伏领跑者火热前行 可靠性护航“长跑”
  5. cobbler工作流分析
  6. Android STL PORT
  7. 自制WiFiPineapple
  8. 开发们 点广告-赚点BT币
  9. IA-32系统编程指南 - 第三章 保护模式的内存管理【2】
  10. Visual Studio怎么使用中文帮助文档