基于 Roslyn 实现一个简单的条件解析引擎
基于 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
- https://github.com/WeihanLi/SamplesInPractice/tree/master/ScriptEngine
- https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples
- https://github.com/dotnetcore/Natasha
基于 Roslyn 实现一个简单的条件解析引擎的更多相关文章
- 自己动手实现一个简单的JSON解析器
1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...
- 一个简单的json解析器
实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...
- VS Extension+NVelocity系列(一)——构建一个简单的NVelocity解析环境
一.前言 本节我们将实际实现一个简单的NVelocity解析环境,以便为以后的实例做一些基本工作,虽然NVelocity如何使用已经属于老掉牙的话题,但我只能专门挑出来一章来做铺垫.人生就是这样无奈啊 ...
- 基于PHP实现一个简单的在线聊天功能(轮询ajax )
基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...
- 用c#自己实现一个简单的JSON解析器
一.JSON格式介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着很多优点.例如易读性更好,占用空间更 ...
- 使用CEF(二)— 基于VS2019编写一个简单CEF样例
使用CEF(二)- 基于VS2019编写一个简单CEF样例 在这一节中,本人将会在Windows下使用VS2019创建一个空白的C++Windows Desktop Application项目,逐步进 ...
- 基于MFC的一个简单计算器
写一个简单的计算器并不是什么很难的事,主要目的是要通过这个程序来学习和分析其中的核心算法.这个简易计算器的核心部分就是对输入的表达式的正确性判断与求值,其中包括对表达式的解析.中缀表达式转后缀表达式. ...
- 基于node实现一个简单的脚手架工具(node控制台交互项目)
实现控制台输入输出 实现文件读写操作 全原生实现一个简单的脚手架工具 实现vue-cli2源码 一.实现控制台输入输出 关于控制台的输入输出依然是基于node进程管理对象process,在proces ...
- 利用Roslyn构建一个简单的C#交互脚本引擎
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...
随机推荐
- LeetCode Day 1
目录 1.TwoSum 2.AddTwoNumbers 4.FindMedianSortedArrays 1.TwoSum 给定一个整数数组nums和一个目标值 target,请你在该数组中找出和为目 ...
- 详解Javascript中的原型与原型链
目录 知识点 参考资料 结束语 知识点 面向对象编程 我们熟悉的Java和C#里,面向对象的两个基本概念是类class和实例instance,而ES6以前的Javascript并没有设计class. ...
- python两个队列实现一个栈和两个栈实现一个队列
1.两个栈实现一个队列 两个栈stack1和stack2, push的时候直接push进stack1,pop时需要判断stack1和stack2中的情况.如果stack2不为空的话,直接从stack2 ...
- ionic3 生命周期钩子
ionViewDidLoad 页面加载完成触发,这里的"加载完成"指的是页面所需的资源已经加载完成,但还没进入这个页面的状态(用户看到的还是上一个页面). 需要注意的是它是一个很傲 ...
- 如果你的unordered_map头文件报错请看这里
请将include<unordered_map>头文件换成下面代码 #if(__cplusplus == 201103L) #include <unordered_map> # ...
- CORS’s source, Principle and Implementation
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源.当一个资源从与该资源 ...
- MyBatis之pageHelper分页插件
1.先导入Maven,jar包依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifa ...
- Flask admin Flask login 整合模板
项目地址: https://github.com/WES6/supflask Flask admin 官方文档: https://flask-admin.readthedocs.io/en/lates ...
- UMD: 通用模块规范
既然CommonJs和AMD风格一样流行,似乎缺少一个统一的规范.所以人们产生了这样的需求,希望有支持两种风格的“通用”模式,于是通用模块规范(UMD)诞生了.
- 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API
https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...