基于 Roslyn 实现一个简单的条件解析引擎

Intro

最近在做一个勋章的服务,我们想定义一些勋章的获取条件,满足条件之后就给用户颁发一个勋章,定义条件的时候会定义需要哪些参数,参数的类型,获取勋章的时候会提供锁需要的参数,有一些内置的参数,内置的参数解析器(ParamResolver)。

最后基于 Roslyn 的 Script + 动态编译功能实现了一个简单的条件解析引擎。

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));
}

实现原理

实现的方式是基于 Roslyn 实现的,核心实现是基于 Roslyn 的 Script 实现的,但是 Roslyn Script 的执行有一些限制,不支持匿名类对象的解析,因此还基于 Roslyn 运行时根据变量信息来动态生成一个类型用于执行脚本解析

var result = await CSharpScript.EvaluateAsync<bool>("1 > 2");

运行时动态生成代码在之前的 DbTool 项目中介绍过,介绍文章 基于 Roslyn 实现动态编译

详细实现细节可以参考代码 https://github.com/WeihanLi/SamplesInPractice/tree/master/ScriptEngine

Memo

程序集加载在 framework 和 core 环境下的差异

实现的时候我们的项目有 dotnetcore 的,还有 netframework 的,这两者加载 dll 的时候略有不同,实现的时候用了一个条件编译,在 dotnet core 环境下和 dotnet framework 分开处理,在 dotnetcore 中使用 AssemblyLoadContext 来加载程序集

#if NETCOREAPP
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath);
#else
var assembly = Assembly.LoadFile(dllPath);
#endif

程序集要保存到文件

原本打算动态生成的程序集保存的一个 Stream 不保存文件,但是实际测试下来必须要保存到文件才可以,所以在项目根目录下创建了一个临时目录 temp 用来保存动态生成的程序集

Roslyn 动态生成的程序集管理

目前还是比较简单的放在一个 temp 目录下了,总觉得每一个类型生成一个程序集有些浪费,但是好像也没办法修改已有程序集,还没找到比较好的解决方案,如果有好的处理方式,欢迎一起交流

More

Natasha 是一个基于 Roslyn 来实现动态编译,能够让你更方便进行动态操作,有动态编译相关需求的可以关注一下这个项目,后面也想用 Natasha 来优化前面提到的问题

基于roslyn的动态编译库,为您提供高效率、高性能、可追踪的动态构建方案,兼容stanadard2.0, 只需原生C#语法不用Emit。 让您的动态方法更加容易编写、跟踪、维护

Reference

基于 Roslyn 实现一个简单的条件解析引擎的更多相关文章

  1. 自己动手实现一个简单的JSON解析器

    1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...

  2. 一个简单的json解析器

    实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...

  3. VS Extension+NVelocity系列(一)——构建一个简单的NVelocity解析环境

    一.前言 本节我们将实际实现一个简单的NVelocity解析环境,以便为以后的实例做一些基本工作,虽然NVelocity如何使用已经属于老掉牙的话题,但我只能专门挑出来一章来做铺垫.人生就是这样无奈啊 ...

  4. 基于PHP实现一个简单的在线聊天功能(轮询ajax )

    基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...

  5. 用c#自己实现一个简单的JSON解析器

    一.JSON格式介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着很多优点.例如易读性更好,占用空间更 ...

  6. 使用CEF(二)— 基于VS2019编写一个简单CEF样例

    使用CEF(二)- 基于VS2019编写一个简单CEF样例 在这一节中,本人将会在Windows下使用VS2019创建一个空白的C++Windows Desktop Application项目,逐步进 ...

  7. 基于MFC的一个简单计算器

    写一个简单的计算器并不是什么很难的事,主要目的是要通过这个程序来学习和分析其中的核心算法.这个简易计算器的核心部分就是对输入的表达式的正确性判断与求值,其中包括对表达式的解析.中缀表达式转后缀表达式. ...

  8. 基于node实现一个简单的脚手架工具(node控制台交互项目)

    实现控制台输入输出 实现文件读写操作 全原生实现一个简单的脚手架工具 实现vue-cli2源码 一.实现控制台输入输出 关于控制台的输入输出依然是基于node进程管理对象process,在proces ...

  9. 利用Roslyn构建一个简单的C#交互脚本引擎

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...

随机推荐

  1. 吴裕雄--天生自然python学习笔记:案例:用正则表达式查找邮件账号

    抓取万水书苑“ ht甲://www.w sbookshow.com/ ”网站中的所有 E-mai l 账号. import requests,re regex = re.compile('[a-zA- ...

  2. 微软推出精简版Windows10Lean系统意欲何为?

    提起Windows系统,自然无须多言都知道它的重要性.在经历了Windows Vista的失败. Windows 7的成功.Windows 8的平庸和Windows 10的复兴之后,微软算是积累下了大 ...

  3. Java IO: PipedOutputStream

    原文链接 作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) PipedOutputStream可以往管道里写入读取字节流数据,代码如下: 01 Outp ...

  4. Mybatis-Plus的分页插件

    使用的是:Mybatis-Plus的分页插件https://baomidou.gitee.io/mybatis-plus-doc/#/?id=%E7%AE%80%E4%BB%8B 1.Mapper.j ...

  5. 吴裕雄--天生自然 R语言开发学习:时间序列

    #-----------------------------------------# # R in Action (2nd ed): Chapter 15 # # Time series # # r ...

  6. Nginx部署前后端分离服务

    飘过... 一,安装Nginx 二,配置nginx 一般nginx配置文件在etc目录下 另,如何找nginx.conf配置文件: 在前后端分离端项目里,前端的代码会被打包成为纯静态文件.使用 Ngi ...

  7. Thomson Plaza里面的三家店以及水果大会

    旅行应该是一个发现的过程,至少我是这么认为的.很多时候并不一定要到什么特别的地方,也可以感受到旅游的乐趣.我觉得只要能看到值得回味的东西就好了.而能回味的东西,往往是需要仔细地来品.像旅行社安排的那样 ...

  8. 递归、尾递归和使用Stream延迟计算优化尾递归

    我们在学数据结构的时候必然会接触栈(Stack),而栈有一个重要的应用是在程序设计语言中实现递归.递归用途十分广泛,比如我们常见的阶乘,如下代码: 1234 public static int (in ...

  9. Oracle介绍

    Published: 2016-11-08 22:15:00 In Data Mining. tags: SQL 版本与配置 企业版 标准版 个人版 事务性数据表 分析型数据表 PL/SQL 配置 控 ...

  10. Yuur persistent XSS

    XSS发生在评论处/帖子正文处 index.php:37-38行 $sql="insert into topic set tid='$tid',title='$title',nickname ...