本质上说,ESQL(及SQL)是表达式,那么程序是怎么表达它的语义的呢?ExpressionTrees。在EF中,它叫CommandTrees,在System.Data.Common.CommandTrees名称空间中。如同System.Linq.Expressions,了解CommandTrees是件有意义的事,也许还能做出神奇的事情。比如下面的CSDL:
<!--m.csdl--> <Schema Namespace="N" xmlns="http://schemas.microsoft.com/ado/2008/09/edm"> <Function Name="FuncA"> <Parameter Name="a" Type="Int32" /> <ReturnType Type="Int32" /> <DefiningExpression> 1 + a * min({2,3,4}) </DefiningExpression> </Function> <Function Name="FuncB"> <ReturnType> <CollectionType> <RowType> <Property Name="P1" Type="String"/> <Property Name="P2" Type="Int32"/> </RowType> </CollectionType> </ReturnType> <DefiningExpression> select e.P1, max(e.P2) as P2 from C.E1Set as e where e.P3 > datetime'2011-01-01 0:0' group by e.P1 having max(e.P2) > 100 order by e.P1 </DefiningExpression> </Function> <EntityType Name="E1"> <Key> <PropertyRef Name="Id" /> </Key> <Property Name="Id" Type="Int32" Nullable="false" /> <Property Name="P1" Type="String" Nullable="false" /> <Property Name="P2" Type="Int32" Nullable="false" /> <Property Name="P3" Type="DateTime" Nullable="false" /> </EntityType> <EntityContainer Name="C"> <EntitySet Name="E1Set" EntityType="N.E1" /> </EntityContainer> </Schema>
如何得到FuncA、FuncB的ESQL的CommandTress?我写了段程序:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Data.Metadata.Edm; using System.Data.Common.CommandTrees; static class InspectingCommandTrees { static DbLambda CompileFuncion(EdmFunction edmFunction, MetadataWorkspace edmWorkspace) { var modelPerspectiveType = Type.GetType("System.Data.Metadata.Edm.ModelPerspective, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); var modelPerspectiveConstructor = modelPerspectiveType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(MetadataWorkspace) }, null); var modelPerspective = modelPerspectiveConstructor.Invoke(new object[] { edmWorkspace }); var parserOptionsType = Type.GetType("System.Data.Common.EntitySql.ParserOptions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); var parserOptionsConstructor = parserOptionsType.GetConstructor(new Type[0]); var parserOptions = parserOptionsConstructor.Invoke(null); var cqlQueryType = Type.GetType("System.Data.Common.EntitySql.CqlQuery, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); var compileQueryCommandLambdaMethod = cqlQueryType.GetMethod("CompileQueryCommandLambda", BindingFlags.NonPublic | BindingFlags.Static); var dbVariableReferenceExpressionConstructor = typeof(DbVariableReferenceExpression).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(TypeUsage), typeof(string) }, null); var dbVariableReferenceExpressions = edmFunction.Parameters.Select(i => (DbVariableReferenceExpression)dbVariableReferenceExpressionConstructor.Invoke(new object[] { i.TypeUsage, i.Name })); return (DbLambda)compileQueryCommandLambdaMethod.Invoke(null, new object[] { edmFunction.CommandTextAttribute, modelPerspective, parserOptions, new DbParameterReferenceExpression[0], dbVariableReferenceExpressions }); } static string PrintExpressionTree(DbExpression edmExpression) { var printMethod = typeof(DbExpression).GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); return (string)printMethod.Invoke(edmExpression, null); } static void Main(string[] args) { try { var edmWorkspace = new MetadataWorkspace(new[] { "m.csdl" }, new Assembly[0]); foreach (var edmFunction in edmWorkspace.GetItems<EdmFunction>(DataSpace.CSpace).Where(i => i.NamespaceName != "Edm")) { Console.WriteLine("=====function {0}=====", edmFunction.Name); var edmLambda = CompileFuncion(edmFunction, edmWorkspace); Console.WriteLine(PrintExpressionTree(edmLambda.Body)); } } catch (Exception ex) { Console.WriteLine(ex); } } }
CommandTrees的打印结果如下:
=====function FuncA===== |_1 |_+ |_ |_Var(a) |_* |_Edm.Min(Collection{Edm.Int32} collection) |_Arguments |_collection |_NewInstance : Collection{Edm.Int32} |_2 |_3 |_4 =====function FuncB===== Project |_Input : '_##sort9' | |_Sort | |_Input : '_##having8' | | |_Filter | | |_Input : '_##group7' | | | |_GroupBy | | | |_Input : '_##geb1', '_##group2' | | | | |_Filter | | | | |_Input : 'e' | | | | | |_Scan : C.E1Set | | | | |_Predicate | | | | |_ | | | | |_Var(e).P3 | | | | |_> | | | | |_2011/1/1 0:00:00 | | | |_Keys | | | | |_Key : 'P1' | | | | |_Var(_##geb1).P1 | | | |_Aggregates | | | |_Aggregate : '_##groupAggMax5' | | | | |_Edm.Max(Collection{Edm.Int32} collection) | | | | |_Arguments | | | | |_collection | | | | |_Var(_##group2).P2 | | | |_Aggregate : '_##groupAggMax6' | | | |_Edm.Max(Collection{Edm.Int32} collection) | | | |_Arguments | | | |_collection | | | |_Var(_##group2).P2 | | |_Predicate | | |_ | | |_Var(_##group7)._##groupAggMax5 | | |_> | | |_100 | |_SortOrder | |_Asc | |_Var(_##having8).P1 |_Projection |_NewInstance : Record['P1'=Edm.String, 'P2'=Edm.Int32] |_Column : 'P1' | |_Var(_##sort9).P1 |_Column : 'P2' |_Var(_##sort9)._##groupAggMax6
慢慢研究吧^_^