基于 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

最新文章

  1. phpStorm入门
  2. mongoDB之Pipeline Aggregation Stages
  3. mvc理解篇
  4. Jquery.Datatables 基本设置的中文注解
  5. [转载] tcp那些事2
  6. 集合类学习之HashMap经典储存 分拣存储与面向对象组合
  7. shell脚本采用crontab定时备份数据库日志
  8. Bitmap介绍
  9. mysql 的密码重置
  10. GitHub版本控制
  11. 你知道src、url、href的全称吗?
  12. 通过后缀名和MIME-TYPE检查实现文件类型校验
  13. Path Analyzer Pro出现raw socket问题
  14. React 与 React Native 底层共识:React 是什么
  15. -bash: 未预期的符号 `(&#39; 附近有语法错误
  16. Maven的配置以及Eclipse的设置
  17. dedecms批量修改文章为待审核稿件怎么操作
  18. 我眼中的Linux系统和红帽RHCE认证
  19. Tensorflow动态seq2seq使用总结(r1.3)
  20. Java多线程之线程阻塞原语LockSupport的使用

热门文章

  1. css3 - transform, transition 与 translate
  2. maven中 pom.xml与properties等配置文件之间互相读取变量
  3. id0-rsa WP合集
  4. 基于Jquery的textarea滚动条插件(原创)
  5. Android开发之《异常处理》
  6. Spring Security基本原理
  7. Eclipse无Server或者Tomcat8.5解决办法
  8. markdoen语法
  9. HTML笔记06--浮动第一章
  10. python爬虫-scrapy日志