使用CellReport实现dotnet集成js脚本功能

首先非常感谢老刀研发的CellReport解决了中小企业报表难的问题。

CellReport的官网地址:https://gitee.com/NoneDay/CellReport 欢迎小伙伴围观;对应的文档地址:http://noneday.gitee.io/cellreport/

CellReport强大的功能,不用我在这里赘述。用过的人都知道。

本文主要记录一下CellReport的脚本功能。

一直以来,都希望有一款趁手的脚本解析和执行器来满足自定义脚本功能,满足不同场景、不同功能的需求。

一个美好的周末,主动联系了一下CellReport的作者(老刀),在老刀热心的指点,不厌其烦的解答后,终于满足了我的小心愿。

言归正传,实现脚本功能的步骤如下:

  • 创建.NET6的项目,并引入CellReport.dll和Antlr3.Runtime.dll;
  • 单独表达式例子:创建ExprFaced2实例,并将参数传入calculate方法中;具体代码如下:
 1 public static bool RunExpress(string express, out IDictionary<Object, Object> resDic)
 2 {
 3     bool ok = false;
 4     resDic = new Dictionary<Object, Object>();
 5     try
 6     {
 7         var ef = new ExprFaced2();
 8         ef.addNewScopeForScript();
 9         var _env = new Env();
10         //固定要加的,脚本运行环境使用环境变量的时候,要找这个变量
11         ef.addVariable("__env__", _env);
12         // 可以直接测试加法
13         //var tt = exprFaced.calculate("=1+2+4 ");
14         //var tt2 = exprFaced.calculate("=iif(11>2,true,false)");
15         var exec_result = ef.calculate($"{express} ;");
16         if (exec_result is Exception ex)
17         {
18             ok = false;
19             throw ex;
20         }
21         resDic = exec_result as IDictionary<Object, Object>;
22         ok = true;
23     }
24     catch (Exception ex)
25     {
26         string err = ex.Message;
27         throw;
28     }
29     return ok;
30 }
  • 脚本例子,同上面不同的地方,我们需要事先定义好执行的脚本,然后再传入脚本对应的参数;

js脚本定义,本脚本定义了两个函数,其中main作为主函数,login_script做为子函数执行对应的业务逻辑

1 function login_script(userid, password) {
2     var _password = password + '-----'
3     return { errcode: 0, message: _password }
4 }
5 function main(userid, password) {
6     return login_script(userid, password)
7 }

封装执行脚本RunScript方法,其中script为脚本内容;varDic为参数字典用户传递参数,并拼接主函数入口的参数个数;resDic为返回的结果字典;expFun为主函数入口;data_source为同数据库交互是指定的数据源(数据源名称、数据源连接串、数据库类型)

 1 public static bool RunScript(string script, Dictionary<string, object> varDic, out IDictionary<Object, Object> resDic, string expFun = "main", DbSource data_source = null)
 2 {
 3     bool ok = false;
 4     resDic = new Dictionary<Object, Object>();
 5     try
 6     {
 7         var exprFaced = new ExprFaced2();
 8         //addNewScopreForScipt是为了载入脚本的。里面的函数仅仅是被解析了,没有被执行
 9          exprFaced.addNewScopeForScript(script);
10         var __env__ = new Env();
11         // 如果需要数据库连接,则在此定义数据源
12         if (data_source != null)
13         {
16             __env__.addDataSource(data_source.Name, data_source.ConnString, data_source.DbType, "0");
17         }
18         //固定要加的,脚本运行环境使用环境变量的时候,要找这个变量
19         exprFaced.addVariable("env", __env__);
20         exprFaced.addVariable("__env__", __env__);
21         exprFaced.addVariable("_user_", null);
22         string funParams = string.Empty;
23         //加自己的预定义变量
24         if (varDic != null && varDic.Count > 0)
25         {
26             foreach (var kv in varDic)
27             {
28                 exprFaced.addVariable(kv.Key, kv.Value);
29             }
30             funParams = string.Join(",", varDic.Keys).ToString();
31         }
42         var exec_result = exprFaced.calculate("{ " + $"{expFun}({funParams});" + "\n}", new());
44         if (exec_result is Exception ex)
45         {
46             ok = false;
47             throw ex;
48         }
49         resDic = exec_result as IDictionary<Object, Object>;
50         ok = true;
51     }
52     catch (Exception ex)
53     {
54         string err = ex.Message;
55         throw;
56     }
57     return ok;
58 }

调用方法(非数据库交互的,不需要传递data_source):

1 string test_user = "admin";
2 string test_password = Guid.NewGuid().ToString();
3 Dictionary<string, object> varDic = new Dictionary<string, object>();
4 varDic.Add("userid", test_user);
5 varDic.Add("password", test_password);
6 IDictionary<Object, Object> resDic = new Dictionary<Object, Object>();
7 var ok = ScriptRun.RunScript(script, varDic, out resDic);

调用成功我们就可以通过resDic获取对应的结果了。

  • 同数据交互的脚本
    • 首先要在Startup.cs中注册数据库对应的dll
    • 如果用到kata,还需要注册Func_kata对应的执行函数;

具体看代码,我这里是winform应用,不过方式一样,没有区别

 1 public static void Main(string[] args)
 2 {
 3     try
 4     {
 5         //DbProviderFactories.RegisterFactory("MySql", MySqlClientFactory.Instance);
 6         //DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", SqliteFactory.Instance);
//此处的名称需要根据CellReportweb端中定义数据源时下拉框保持一致
7 DbProviderFactories.RegisterFactory("SqlClient", SqlClientFactory.Instance);
//如果需要用到kata脚本的时候才需要调用
8 CreatHostBuilder(args).Build(); 9 ApplicationConfiguration.Initialize(); 10 Application.Run(new Form1()); 11 } 12 catch (Exception) 13 { 14 throw; 15 } 16 } 17 public static IHostBuilder CreatHostBuilder(string[] args) => 18 Host.CreateDefaultBuilder(args) 19 .ConfigureServices((hostContext, services) => 20 { 21 CellReport.core.expr.ExprHelper.buildFuncMap(); 22 //CellReport.core.expr.ExprHelper.AddFunc(typeof(CellReport.function.Func_md5)); 23 //CellReport.core.expr.ExprHelper.AddFunc(typeof(CellReport.function.Func_qr_code)); 24 CellReport.core.expr.ExprHelper.AddFunc(typeof(CellReport.function.Func_kata)); 25 CellReport.core.expr.ExprHelper.AddFunc(typeof(CellReport.function.Func_kata_Variable)); 26 });
 1 function main(dataSource, tableName, fieldNames) {
 2     var db = openDb(`${dataSource}`)
 3     var data = null
 4     if (fieldNames == null || fieldNames == '')
 5         data = db.select(`select * from  ${tableName} ;`)
 6     else 
 7         data = db.select(`select ${fieldNames}  from  ${tableName} ;`)
 8     db.close()
 9     return { data }
10 }
1 Dictionary<string, object> varDic = new Dictionary<string, object>();
2 varDic.Add("dataSource", SQL.Name);
3 varDic.Add("tableName", "iot_device");
4 varDic.Add("fieldNames", null);
5 IDictionary<Object, Object> resDic = new Dictionary<Object, Object>();
6 var ok = ScriptRun.RunScript(script, varDic, out resDic, "main", SQL);
SQL为DataSource,具体定义为:
DbSource SQL = new DbSource
{
    Name = "KIT",
    DbType = "SqlClient",
    ConnString = "Data Source=127.0.0.1;Initial Catalog=xx;Persist Security Info=True;User ID=sa;Password=xxx;Min Pool Size=1;Max Pool Size=50;Connect Timeout=15000;"
};
  • kata脚本如下:,调用函数和方法同上面保持一致
 1 function main(dataSource, tableName, fieldNames) {
 2     var db = kata(`${dataSource}`)
 3     var data = db
 4         .Query('iot_device')
 5         .Get()
 6         .select((x) => {
 7             return { id: x.id, device_name: x.device_name }
 8         })
 9     return { data }
10 }

注意事项:

 1 <Reference Include="CellReport">
 2     <HintPath>.\libs\CellReport.dll</HintPath>
 3 </Reference>
 4 <Reference Include="Antlr3.Runtime">
 5     <HintPath>.\libs\Antlr3.Runtime.dll</HintPath>
 6 </Reference>
 7 <Reference Include="MySql.Data">
 8     <HintPath>.\libs\MySql.Data.dll</HintPath>
 9 </Reference>
10 <Reference Include="Microsoft.Data.Sqlite">
11     <HintPath>.\libs\Microsoft.Data.Sqlite.dll</HintPath>
12 </Reference>
13 <Reference Include="SqlKata">
14     <HintPath>.\libs\SqlKata.dll</HintPath>
15 </Reference>
16 <Reference Include="SqlKata.Execution">
17     <HintPath>.\libs\SqlKata.Execution.dll</HintPath>
18 </Reference>
19 <Reference Include="Dapper">
20     <HintPath>.\libs\Dapper.dll</HintPath>
21 </Reference>
22 <Reference Include="Humanizer">
23     <HintPath>.\libs\Humanizer.dll</HintPath>
24 </Reference>

 如果要自定处理函数,参考如下:

 

 cs文件必须要以Func_开头,目前没有实际使用。

posted @ 2022-12-12 16:48  egreen  阅读(453)  评论(0编辑  收藏  举报