@@ -884,6 +884,29 @@ private static SqlObjectProperty[] CreateInitializers(ReadOnlyCollection<Express
884
884
result [ i ] = prop ;
885
885
}
886
886
887
+ return result ;
888
+ }
889
+
890
+ private static SqlSelectItem [ ] CreateSelectItems ( ReadOnlyCollection < Expression > arguments , ReadOnlyCollection < MemberInfo > members , TranslationContext context )
891
+ {
892
+ if ( arguments . Count != members . Count )
893
+ {
894
+ throw new InvalidOperationException ( "Expected same number of arguments as members" ) ;
895
+ }
896
+
897
+ SqlSelectItem [ ] result = new SqlSelectItem [ arguments . Count ] ;
898
+ for ( int i = 0 ; i < arguments . Count ; i ++ )
899
+ {
900
+ Expression arg = arguments [ i ] ;
901
+ MemberInfo member = members [ i ] ;
902
+ SqlScalarExpression selectExpression = ExpressionToSql . VisitScalarExpression ( arg , context ) ;
903
+
904
+ string memberName = member . GetMemberName ( context ) ;
905
+ SqlIdentifier alias = SqlIdentifier . Create ( memberName ) ;
906
+ SqlSelectItem prop = SqlSelectItem . Create ( selectExpression , alias ) ;
907
+ result [ i ] = prop ;
908
+ }
909
+
887
910
return result ;
888
911
}
889
912
@@ -1314,6 +1337,80 @@ private static Collection VisitMethodCall(MethodCallExpression inputExpression,
1314
1337
context . PopMethod ( ) ;
1315
1338
return result ;
1316
1339
}
1340
+
1341
+ /// <summary>
1342
+ /// Visit a method call, construct the corresponding query and return the select clause for the aggregate function.
1343
+ /// At ExpressionToSql point only LINQ method calls are allowed.
1344
+ /// These methods are static extension methods of IQueryable or IEnumerable.
1345
+ /// </summary>
1346
+ /// <param name="inputExpression">Method to translate.</param>
1347
+ /// <param name="context">Query translation context.</param>
1348
+ private static SqlSelectClause VisitGroupByAggregateMethodCall ( MethodCallExpression inputExpression , TranslationContext context )
1349
+ {
1350
+ context . PushMethod ( inputExpression ) ;
1351
+
1352
+ Type declaringType = inputExpression . Method . DeclaringType ;
1353
+ if ( ( declaringType != typeof ( Queryable ) && declaringType != typeof ( Enumerable ) )
1354
+ || ! inputExpression . Method . IsStatic )
1355
+ {
1356
+ throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . OnlyLINQMethodsAreSupported , inputExpression . Method . Name ) ) ;
1357
+ }
1358
+
1359
+ if ( inputExpression . Object != null )
1360
+ {
1361
+ throw new DocumentQueryException ( ClientResources . ExpectedMethodCallsMethods ) ;
1362
+ }
1363
+
1364
+ Expression inputCollection = inputExpression . Arguments [ 0 ] ; // all these methods are static extension methods, so argument[0] is the collection
1365
+
1366
+ Collection collection = ExpressionToSql . Translate ( inputCollection , context ) ;
1367
+ context . PushCollection ( collection ) ;
1368
+
1369
+ bool shouldBeOnNewQuery = context . CurrentQuery . ShouldBeOnNewQuery ( inputExpression . Method . Name , inputExpression . Arguments . Count ) ;
1370
+ context . PushSubqueryBinding ( shouldBeOnNewQuery ) ;
1371
+
1372
+ if ( context . LastExpressionIsGroupBy )
1373
+ {
1374
+ throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , "Group By cannot be followed by other methods" ) ) ;
1375
+ }
1376
+
1377
+ SqlSelectClause select ;
1378
+ switch ( inputExpression . Method . Name )
1379
+ {
1380
+ case LinqMethods . Average :
1381
+ {
1382
+ select = ExpressionToSql . VisitAggregateFunction ( inputExpression . Arguments , context , SqlFunctionCallScalarExpression . Names . Avg ) ;
1383
+ break ;
1384
+ }
1385
+ case LinqMethods . Count :
1386
+ {
1387
+ select = ExpressionToSql . VisitCount ( inputExpression . Arguments , context ) ;
1388
+ break ;
1389
+ }
1390
+ case LinqMethods . Max :
1391
+ {
1392
+ select = ExpressionToSql . VisitAggregateFunction ( inputExpression . Arguments , context , SqlFunctionCallScalarExpression . Names . Max ) ;
1393
+ break ;
1394
+ }
1395
+ case LinqMethods . Min :
1396
+ {
1397
+ select = ExpressionToSql . VisitAggregateFunction ( inputExpression . Arguments , context , SqlFunctionCallScalarExpression . Names . Min ) ;
1398
+ break ;
1399
+ }
1400
+ case LinqMethods . Sum :
1401
+ {
1402
+ select = ExpressionToSql . VisitAggregateFunction ( inputExpression . Arguments , context , SqlFunctionCallScalarExpression . Names . Sum ) ;
1403
+ break ;
1404
+ }
1405
+ default :
1406
+ throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . MethodNotSupported , inputExpression . Method . Name ) ) ;
1407
+ }
1408
+
1409
+ context . PopSubqueryBinding ( ) ;
1410
+ context . PopCollection ( ) ;
1411
+ context . PopMethod ( ) ;
1412
+ return select ;
1413
+ }
1317
1414
1318
1415
/// <summary>
1319
1416
/// Determine if an expression should be translated to a subquery.
@@ -1735,48 +1832,93 @@ private static Collection VisitGroupBy(Type returnElementType, ReadOnlyCollectio
1735
1832
switch ( valueSelectorExpression . NodeType )
1736
1833
{
1737
1834
case ExpressionType . Constant :
1738
- {
1739
- ConstantExpression constantExpression = ( ConstantExpression ) valueSelectorExpression ;
1740
- SqlScalarExpression selectExpression = ExpressionToSql . VisitConstant ( constantExpression , context ) ;
1741
-
1742
- SqlSelectSpec sqlSpec = SqlSelectValueSpec . Create ( selectExpression ) ;
1743
- SqlSelectClause select = SqlSelectClause . Create ( sqlSpec , null ) ;
1744
- context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1745
- break ;
1746
- }
1835
+ {
1836
+ ConstantExpression constantExpression = ( ConstantExpression ) valueSelectorExpression ;
1837
+ SqlScalarExpression selectExpression = ExpressionToSql . VisitConstant ( constantExpression , context ) ;
1838
+
1839
+ SqlSelectSpec sqlSpec = SqlSelectValueSpec . Create ( selectExpression ) ;
1840
+ SqlSelectClause select = SqlSelectClause . Create ( sqlSpec , null ) ;
1841
+ context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1842
+ break ;
1843
+ }
1747
1844
case ExpressionType . Parameter :
1748
- {
1749
- ParameterExpression parameterValueExpression = ( ParameterExpression ) valueSelectorExpression ;
1750
- SqlScalarExpression selectExpression = ExpressionToSql . VisitParameter ( parameterValueExpression , context ) ;
1845
+ {
1846
+ ParameterExpression parameterValueExpression = ( ParameterExpression ) valueSelectorExpression ;
1847
+ SqlScalarExpression selectExpression = ExpressionToSql . VisitParameter ( parameterValueExpression , context ) ;
1751
1848
1752
- SqlSelectSpec sqlSpec = SqlSelectValueSpec . Create ( selectExpression ) ;
1753
- SqlSelectClause select = SqlSelectClause . Create ( sqlSpec , null ) ;
1754
- context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1755
- break ;
1756
- }
1849
+ SqlSelectSpec sqlSpec = SqlSelectValueSpec . Create ( selectExpression ) ;
1850
+ SqlSelectClause select = SqlSelectClause . Create ( sqlSpec , null ) ;
1851
+ context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1852
+ break ;
1853
+ }
1757
1854
case ExpressionType . Call :
1758
- {
1759
- // Single Value Selector
1760
- MethodCallExpression methodCallExpression = ( MethodCallExpression ) valueSelectorExpression ;
1761
- switch ( methodCallExpression . Method . Name )
1762
1855
{
1763
- case LinqMethods . Max :
1764
- case LinqMethods . Min :
1765
- case LinqMethods . Average :
1766
- case LinqMethods . Count :
1767
- case LinqMethods . Sum :
1768
- ExpressionToSql . VisitMethodCall ( methodCallExpression , context ) ;
1769
- break ;
1770
- default :
1771
- throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . MethodNotSupported , methodCallExpression . Method . Name ) ) ;
1772
- }
1856
+ // Single Value Selector
1857
+ MethodCallExpression methodCallExpression = ( MethodCallExpression ) valueSelectorExpression ;
1858
+ SqlSelectClause select = ExpressionToSql . VisitGroupByAggregateMethodCall ( methodCallExpression , context ) ;
1859
+ context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1860
+ break ;
1861
+ }
1862
+ case ExpressionType . New :
1863
+ {
1864
+ // Add select item clause at the end of this method
1865
+ NewExpression newExpression = ( NewExpression ) valueSelectorExpression ;
1773
1866
1774
- break ;
1775
- }
1776
- case ExpressionType . New :
1777
- // TODO: Multi Value Selector
1778
- throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . ExpressionTypeIsNotSupported , ExpressionType . New ) ) ;
1779
-
1867
+ if ( newExpression . Members == null )
1868
+ {
1869
+ throw new DocumentQueryException ( ClientResources . ConstructorInvocationNotSupported ) ;
1870
+ }
1871
+
1872
+ // Get the list of items and the bindings
1873
+ ReadOnlyCollection < Expression > newExpressionArguments = newExpression . Arguments ;
1874
+ ReadOnlyCollection < MemberInfo > newExpressionMembers = newExpression . Members ;
1875
+
1876
+ SqlSelectItem [ ] selectItems = new SqlSelectItem [ newExpressionArguments . Count ] ;
1877
+ for ( int i = 0 ; i < newExpressionArguments . Count ; i ++ )
1878
+ {
1879
+ MemberInfo member = newExpressionMembers [ i ] ;
1880
+ string memberName = member . GetMemberName ( context ) ;
1881
+ SqlIdentifier alias = SqlIdentifier . Create ( memberName ) ;
1882
+
1883
+ Expression arg = newExpressionArguments [ i ] ;
1884
+ switch ( arg . NodeType )
1885
+ {
1886
+ case ExpressionType . Constant :
1887
+ {
1888
+ SqlScalarExpression selectExpression = ExpressionToSql . VisitConstant ( ( ConstantExpression ) arg , context ) ;
1889
+
1890
+ SqlSelectItem prop = SqlSelectItem . Create ( selectExpression , alias ) ;
1891
+ selectItems [ i ] = prop ;
1892
+ break ;
1893
+ }
1894
+ case ExpressionType . Parameter :
1895
+ {
1896
+ SqlScalarExpression selectExpression = ExpressionToSql . VisitParameter ( ( ParameterExpression ) arg , context ) ;
1897
+
1898
+ SqlSelectItem prop = SqlSelectItem . Create ( selectExpression , alias ) ;
1899
+ selectItems [ i ] = prop ;
1900
+ break ;
1901
+ }
1902
+ case ExpressionType . Call :
1903
+ {
1904
+ SqlSelectClause selectClause = ExpressionToSql . VisitGroupByAggregateMethodCall ( ( MethodCallExpression ) arg , context ) ;
1905
+ SqlScalarExpression selectExpression = ( ( SqlSelectValueSpec ) selectClause . SelectSpec ) . Expression ;
1906
+
1907
+ SqlSelectItem prop = SqlSelectItem . Create ( selectExpression , alias ) ;
1908
+ selectItems [ i ] = prop ;
1909
+ break ;
1910
+ }
1911
+ default :
1912
+ throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . ExpressionTypeIsNotSupported , arg . NodeType ) ) ;
1913
+ }
1914
+ }
1915
+
1916
+ SqlSelectListSpec sqlSpec = SqlSelectListSpec . Create ( selectItems ) ;
1917
+ SqlSelectClause select = SqlSelectClause . Create ( sqlSpec , null ) ;
1918
+ context . CurrentQuery = context . CurrentQuery . AddSelectClause ( select , context ) ;
1919
+
1920
+ break ;
1921
+ }
1780
1922
default :
1781
1923
throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . ExpressionTypeIsNotSupported , valueSelectorExpression . NodeType ) ) ;
1782
1924
}
0 commit comments