news 2026/6/26 1:13:43

AScript如何实现LINQ语法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AScript如何实现LINQ语法

electMany/Where/Join/GroupJoin/GroupBy/OrderBy/OrderByDescending/Select等方法。

Queryable/Enumerable扩展方法已通过AddFunc方式注入到了CSharpLang语言中:

1 // IEnumerable<T>扩展方法 2 AddFunc(typeof(System.Linq.Enumerable)); 3 // IQueryable<T>扩展方法 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 Dictionary<string, string> _VarParentDict = new Dictionary<string, 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为聚合参数,ab是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__AnonymousType0`2(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
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 1:13:07

Appium跨界Windows桌面自动化测试:统一技术栈实战指南

1. 项目概述&#xff1a;当Appium遇上Windows桌面提到Appium&#xff0c;绝大多数测试工程师和自动化开发者的第一反应就是移动端自动化测试。没错&#xff0c;从Android到iOS&#xff0c;Appium凭借其跨平台、支持多语言的特性&#xff0c;早已成为移动端UI自动化的首选框架。…

作者头像 李华
网站建设 2026/6/26 1:11:37

2026深度实测|TRAE与Cursor中文vibe coding迭代能力全对比

这篇文章不按工具逐个介绍&#xff0c;而是按开发者的真实一天来组织&#xff1a;从早上改bug到晚上写新功能&#xff0c;5款工具在每个环节的表现。作为CS研二在读实习生&#xff0c;我在社区论坛项目&#xff08;项目代号&#xff1a;Forum-007&#xff09;中深度使用TRAE与C…

作者头像 李华
网站建设 2026/6/26 1:03:23

类变量和实例变量的内存分配方式对性能的影响具体有哪些?

类变量、实例变量内存分配方式带来的五大具体性能影响一、内存占用性能1. 类变量类定义阶段在堆中只分配唯一一块内存&#xff0c;存放在类对象的__dict__里&#xff0c;所有实例仅保存对这块内存的引用地址&#xff0c;不会复制数据。优点&#xff1a;海量实例场景下内存高度复…

作者头像 李华
网站建设 2026/6/26 1:02:40

MuleSoft+LLM企业级AI编排:实时数据编织与可治理工作流

1. 项目概述&#xff1a;当企业级集成平台遇上大语言模型&#xff0c;不是拼接&#xff0c;而是重定义工作流“AI Orchestration in Action: How MuleSoft and LLMs Fuel the Future of Enterprise AI”——这个标题里藏着一个正在发生的、静默却剧烈的范式转移。它说的不是“用…

作者头像 李华