@@ -884,6 +884,29 @@ private static SqlObjectProperty[] CreateInitializers(ReadOnlyCollection<Express
884884 result [ i ] = prop ;
885885 }
886886
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+
887910 return result ;
888911 }
889912
@@ -1314,6 +1337,80 @@ private static Collection VisitMethodCall(MethodCallExpression inputExpression,
13141337 context . PopMethod ( ) ;
13151338 return result ;
13161339 }
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+ }
13171414
13181415 /// <summary>
13191416 /// Determine if an expression should be translated to a subquery.
@@ -1735,48 +1832,93 @@ private static Collection VisitGroupBy(Type returnElementType, ReadOnlyCollectio
17351832 switch ( valueSelectorExpression . NodeType )
17361833 {
17371834 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+ }
17471844 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 ) ;
17511848
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+ }
17571854 case ExpressionType . Call :
1758- {
1759- // Single Value Selector
1760- MethodCallExpression methodCallExpression = ( MethodCallExpression ) valueSelectorExpression ;
1761- switch ( methodCallExpression . Method . Name )
17621855 {
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 ;
17731866
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+ }
17801922 default :
17811923 throw new DocumentQueryException ( string . Format ( CultureInfo . CurrentCulture , ClientResources . ExpressionTypeIsNotSupported , valueSelectorExpression . NodeType ) ) ;
17821924 }
0 commit comments