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为聚合参数,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__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