AScript如何实现LINQ语法

发布时间:2026/6/26 1:13:54

AScript如何实现LINQ语法 electMany/Where/Join/GroupJoin/GroupBy/OrderBy/OrderByDescending/Select等方法。Queryable/Enumerable扩展方法已通过AddFunc方式注入到了CSharpLang语言中1 // IEnumerableT扩展方法 2 AddFunc(typeof(System.Linq.Enumerable)); 3 // IQueryableT扩展方法 4 AddFunc(typeof(System.Linq.Queryable));LINQ To SQL通过分析Queryable扩展方法及Lambda参数生成对应的SQL语句来执行数据库查询操作。我们只要实现从LINQ语句生成调用Queryable/Enumerable扩展方法即可。二、示例1 using (var context new TestSqliteContext()) 2 { 3 string s 4 var q from p in context.Persons 5 join a in context.AddressInfos on p.Id equals a.UserId into aa 6 from a in aa.DefaultIfEmpty() 7 select new { p.Id, p.Name, p.Age, MyAddress a.Address }; 8 q.ToList(); 9 ; 10 var script new Script(); 11 script.Context.SetVar(context, context); 12 var list script.Eval(s); 13 Console.WriteLine(JsonConvert.SerializeObject(list, Formatting.Indented)); 14 }生成的sqlite查询语句为1 SELECT p.Id, p.Name, p.Age, a.Address AS MyAddress 2 FROM Persons AS p 3 LEFT JOIN AddressInfos AS a ON p.Id a.UserId下面我们来看看如何实现LINQ语法。三、定义QueryNode节点首先定义一个LINQ查询语法树节点用于管理LINQ语法中的from/where/join/group/orderby/select语句并在执行或编译时生成对应的方法调用。1 public class QueryNode : TreeNode 2 { 3 // 变量所属上级变量聚合 4 private readonly Dictionarystring, string _VarParentDict new Dictionarystring, string(); 5 // 变量所属上级计数变量聚合计数 6 private int _ParentCounter 0; 7 // 当前变量名 8 private string _CurrentVarName; 9 // 当前数据源 10 private ITreeNode _Source; 11 public override Expression Build(BuildContext buildContext, ScriptContext scriptContext, BuildOptions options) 12 { 13 return _Source.Build(buildContext, scriptContext, options); 14 } 15 public override object Eval(ScriptContext context, BuildOptions options, EvalControl control, out Type returnType) 16 { 17 return _Source.Eval(context, options, control, out returnType); 18 } 19 }1、添加from语句在QueryNode中添加AddFrom方法增加LINQ中的from语句1 /// summary 2 /// from varName in source 3 /// /summary 4 public void AddFrom(string varName, ITreeNode source) 5 { 6 if (_Source null) 7 { 8 // 第1个from语句 9 _Source source; 10 _CurrentVarName varName; 11 return; 12 } 13 // _Source.SelectMany(_CurrentVarName source, (_CurrentVarName, varName) new { _CurrentVarName, varName }) 14 var selectMany new CallFuncNode 15 { 16 Name SelectMany, 17 Args new ITreeNode[] 18 { 19 _Source, 20 // _CurrentVarName source 21 new DefineFuncNode 22 { 23 Args new [] { new DefineVarNode(_CurrentVarName) }, 24 Body TryVisitAndReplace(source) 25 }, 26 // (_CurrentVarName, varName) new { _CurrentVarName, varName } 27 new DefineFuncNode 28 { 29 Args new [] 30 { 31 new DefineVarNode(_CurrentVarName) }, 32 new DefineVarNode(varName) } 33 }, 34 Body new NewNode 35 { 36 InitProperties new ITreeNode[] 37 { 38 new VariableNode(_CurrentVarName), 39 new VariableNode(varName) 40 } 41 } 42 } 43 } 44 }; 45 // 更新当前数据源 46 _Source selectMany; 47 // 变量聚合 48 var oldCurrentName _CurrentVarName; 49 _CurrentVarName $h__TransparentIdentifier{_ParentCounter}; 50 _VarParentDict[oldCurrentName] _CurrentVarName; 51 _VarParentDict[varName] _CurrentVarName; 52 }多个from语句生成SelectMany方法调用方法调用对应的是CallFuncNode节点Lambda参数或者Func参数对应的是DefineFuncNode节点。SelectMany方法第2个参数为变量聚合所以我们需要生成聚合变量名并跟踪当前变量聚合路径TryVisitAndReplace方法用于替换数据源中的变量聚合路径这里不作说明详情可查看QueryNode源码。2、添加where语句1 /// summary 2 /// from a in query1 3 /// from b in query2 4 /// where a.Age b.Age 5 /// /summary 6 public void AddWhere(ITreeNode condition) 7 { 8 if (_Source null) 9 { 10 throw new Exceptions.ScriptAnalyzingException(invalid expression where); 11 } 12 // _Source.Where(h__TransparentIdentifier0 (h__TransparentIdentifier0.a.Age h__TransparentIdentifier0.b.Age)) 13 var whereNode new CallFuncNode 14 { 15 Name Where, 16 Args new ITreeNode[] 17 { 18 _Source, 19 // h__TransparentIdentifier0 (h__TransparentIdentifier0.a.Age h__TransparentIdentifier0.b.Age) 20 new DefineFuncNode 21 { 22 Args new [] { new DefineVarNode(_CurrentVarName) }, 23 Body TryVisitAndReplace(condition) 24 } 25 } 26 }; 27 // 更新当前数据源 28 _Source whereNode; 29 }where语句生成Where方法调用h__TransparentIdentifier0为聚合参数a和b是where语句中的参数TryVisitAndReplace方法就是把where语句中的a、b参数替换为聚合参数路径h__TransparentIdentifier0.a和h__TransparentIdentifier0.b。3、添加select语句1 /// summary 2 /// select new { a.Name, b.Age } 3 /// /summary 4 public void AddSelect(ITreeNode selector) 5 { 6 if (_Source null) 7 { 8 throw new Exceptions.ScriptAnalyzingException(invalid expression select); 9 } 10 // _Source.Select(h__TransparentIdentifier0 new f__AnonymousType02(Name h__TransparentIdentifier0.a.Name, Age h__TransparentIdentifier0.b.Age)) 11 var selectNode new CallFuncNode 12 { 13 Name Select, 14 Args new ITreeNode[] 15 { 16 _Source, 17 new DefineFuncNode

相关新闻