PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Web.Http.OData/OData/Query/Expressions/FilterBinder.cs

https://github.com/huyq2002/aspnetwebstack
C# | 1383 lines | 1068 code | 231 blank | 84 comment | 277 complexity | 0a598f73c8682ab63c9a16d5270113a8 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.Collections.Generic;
  3. using System.Data.Linq;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Diagnostics.Contracts;
  6. using System.Globalization;
  7. using System.Linq;
  8. using System.Linq.Expressions;
  9. using System.Reflection;
  10. using System.Runtime.CompilerServices;
  11. using System.Web.Http.Dispatcher;
  12. using System.Web.Http.OData.Formatter;
  13. using System.Web.Http.OData.Properties;
  14. using System.Xml.Linq;
  15. using Microsoft.Data.Edm;
  16. using Microsoft.Data.OData;
  17. using Microsoft.Data.OData.Query;
  18. using Microsoft.Data.OData.Query.SemanticAst;
  19. namespace System.Web.Http.OData.Query.Expressions
  20. {
  21. /// <summary>
  22. /// Translates an OData $filter parse tree represented by <see cref="FilterClause"/> to
  23. /// an <see cref="Expression"/> and applies it to an <see cref="IQueryable"/>.
  24. /// </summary>
  25. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Relies on many ODataLib classes.")]
  26. internal class FilterBinder
  27. {
  28. private const string ODataItParameterName = "$it";
  29. private static readonly MethodInfo _stringCompareMethodInfo = typeof(string).GetMethod("Compare", new[] { typeof(string), typeof(string), typeof(StringComparison) });
  30. private static readonly Expression _nullConstant = Expression.Constant(null);
  31. private static readonly Expression _falseConstant = Expression.Constant(false);
  32. private static readonly Expression _trueConstant = Expression.Constant(true);
  33. private static readonly Expression _zeroConstant = Expression.Constant(0);
  34. private static readonly Expression _ordinalStringComparisonConstant = Expression.Constant(StringComparison.Ordinal);
  35. private static Dictionary<BinaryOperatorKind, ExpressionType> _binaryOperatorMapping = new Dictionary<BinaryOperatorKind, ExpressionType>
  36. {
  37. { BinaryOperatorKind.Add, ExpressionType.Add },
  38. { BinaryOperatorKind.And, ExpressionType.AndAlso },
  39. { BinaryOperatorKind.Divide, ExpressionType.Divide },
  40. { BinaryOperatorKind.Equal, ExpressionType.Equal },
  41. { BinaryOperatorKind.GreaterThan, ExpressionType.GreaterThan },
  42. { BinaryOperatorKind.GreaterThanOrEqual, ExpressionType.GreaterThanOrEqual },
  43. { BinaryOperatorKind.LessThan, ExpressionType.LessThan },
  44. { BinaryOperatorKind.LessThanOrEqual, ExpressionType.LessThanOrEqual },
  45. { BinaryOperatorKind.Modulo, ExpressionType.Modulo },
  46. { BinaryOperatorKind.Multiply, ExpressionType.Multiply },
  47. { BinaryOperatorKind.NotEqual, ExpressionType.NotEqual },
  48. { BinaryOperatorKind.Or, ExpressionType.OrElse },
  49. { BinaryOperatorKind.Subtract, ExpressionType.Subtract },
  50. };
  51. private IEdmModel _model;
  52. private Stack<Dictionary<string, ParameterExpression>> _parametersStack;
  53. private Dictionary<string, ParameterExpression> _lambdaParameters;
  54. private ODataQuerySettings _querySettings;
  55. private IAssembliesResolver _assembliesResolver;
  56. private FilterBinder(IEdmModel model, IAssembliesResolver assembliesResolver, ODataQuerySettings querySettings)
  57. : this(model, querySettings)
  58. {
  59. _assembliesResolver = assembliesResolver;
  60. }
  61. private FilterBinder(IEdmModel model, ODataQuerySettings querySettings)
  62. {
  63. Contract.Assert(model != null);
  64. Contract.Assert(querySettings != null);
  65. Contract.Assert(querySettings.HandleNullPropagation != HandleNullPropagationOption.Default);
  66. _querySettings = querySettings;
  67. _parametersStack = new Stack<Dictionary<string, ParameterExpression>>();
  68. _model = model;
  69. }
  70. public static Expression<Func<TEntityType, bool>> Bind<TEntityType>(FilterClause filterClause, IEdmModel model,
  71. IAssembliesResolver assembliesResolver, ODataQuerySettings querySettings)
  72. {
  73. return Bind(filterClause, typeof(TEntityType), model, assembliesResolver, querySettings) as Expression<Func<TEntityType, bool>>;
  74. }
  75. public static Expression Bind(FilterClause filterClause, Type filterType, IEdmModel model,
  76. IAssembliesResolver assembliesResolver, ODataQuerySettings querySettings)
  77. {
  78. if (filterClause == null)
  79. {
  80. throw Error.ArgumentNull("filterClause");
  81. }
  82. if (filterType == null)
  83. {
  84. throw Error.ArgumentNull("filterType");
  85. }
  86. if (model == null)
  87. {
  88. throw Error.ArgumentNull("model");
  89. }
  90. if (assembliesResolver == null)
  91. {
  92. throw Error.ArgumentNull("assembliesResolver");
  93. }
  94. FilterBinder binder = new FilterBinder(model, assembliesResolver, querySettings);
  95. LambdaExpression filter = binder.BindExpression(filterClause.Expression, filterClause.RangeVariable, filterType);
  96. filter = Expression.Lambda(binder.ApplyNullPropagationForFilterBody(filter.Body), filter.Parameters);
  97. Type expectedFilterType = typeof(Func<,>).MakeGenericType(filterType, typeof(bool));
  98. if (filter.Type != expectedFilterType)
  99. {
  100. throw Error.Argument("filterType", SRResources.CannotCastFilter, filter.Type.FullName, expectedFilterType.FullName);
  101. }
  102. return filter;
  103. }
  104. public static LambdaExpression Bind(OrderByClause orderBy, Type elementType,
  105. IEdmModel model, ODataQuerySettings querySettings)
  106. {
  107. Contract.Assert(orderBy != null);
  108. Contract.Assert(elementType != null);
  109. Contract.Assert(model != null);
  110. Contract.Assert(querySettings != null);
  111. FilterBinder binder = new FilterBinder(model, querySettings);
  112. LambdaExpression orderByLambda = binder.BindExpression(orderBy.Expression, orderBy.RangeVariable, elementType);
  113. return orderByLambda;
  114. }
  115. private Expression Bind(QueryNode node)
  116. {
  117. // Recursion guard to avoid stack overflows
  118. RuntimeHelpers.EnsureSufficientExecutionStack();
  119. CollectionNode collectionNode = node as CollectionNode;
  120. SingleValueNode singleValueNode = node as SingleValueNode;
  121. if (collectionNode != null)
  122. {
  123. switch (node.Kind)
  124. {
  125. case QueryNodeKind.CollectionNavigationNode:
  126. CollectionNavigationNode navigationNode = node as CollectionNavigationNode;
  127. return BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty);
  128. case QueryNodeKind.CollectionPropertyAccess:
  129. return BindCollectionPropertyAccessNode(node as CollectionPropertyAccessNode);
  130. case QueryNodeKind.EntityCollectionCast:
  131. return BindEntityCollectionCastNode(node as EntityCollectionCastNode);
  132. default:
  133. throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, node.Kind, typeof(FilterBinder).Name);
  134. }
  135. }
  136. else if (singleValueNode != null)
  137. {
  138. switch (node.Kind)
  139. {
  140. case QueryNodeKind.BinaryOperator:
  141. return BindBinaryOperatorNode(node as BinaryOperatorNode);
  142. case QueryNodeKind.Constant:
  143. return BindConstantNode(node as ConstantNode);
  144. case QueryNodeKind.Convert:
  145. return BindConvertNode(node as ConvertNode);
  146. case QueryNodeKind.EntityRangeVariableReference:
  147. return BindRangeVariable((node as EntityRangeVariableReferenceNode).RangeVariable);
  148. case QueryNodeKind.NonentityRangeVariableReference:
  149. return BindRangeVariable((node as NonentityRangeVariableReferenceNode).RangeVariable);
  150. case QueryNodeKind.SingleValuePropertyAccess:
  151. return BindPropertyAccessQueryNode(node as SingleValuePropertyAccessNode);
  152. case QueryNodeKind.UnaryOperator:
  153. return BindUnaryOperatorNode(node as UnaryOperatorNode);
  154. case QueryNodeKind.SingleValueFunctionCall:
  155. return BindSingleValueFunctionCallNode(node as SingleValueFunctionCallNode);
  156. case QueryNodeKind.SingleNavigationNode:
  157. SingleNavigationNode navigationNode = node as SingleNavigationNode;
  158. return BindNavigationPropertyNode(navigationNode.Source, navigationNode.NavigationProperty);
  159. case QueryNodeKind.Any:
  160. return BindAnyNode(node as AnyNode);
  161. case QueryNodeKind.All:
  162. return BindAllNode(node as AllNode);
  163. case QueryNodeKind.SingleEntityCast:
  164. return BindSingleEntityCastNode(node as SingleEntityCastNode);
  165. default:
  166. throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, node.Kind, typeof(FilterBinder).Name);
  167. }
  168. }
  169. else
  170. {
  171. throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, node.Kind, typeof(FilterBinder).Name);
  172. }
  173. }
  174. private Expression BindSingleEntityCastNode(SingleEntityCastNode node)
  175. {
  176. IEdmEntityTypeReference entity = node.EntityTypeReference;
  177. Contract.Assert(entity != null, "NS casts can contain only entity types");
  178. Type clrType = EdmLibHelpers.GetClrType(entity, _model);
  179. Expression source = BindCastSourceNode(node.Source);
  180. return Expression.TypeAs(source, clrType);
  181. }
  182. private Expression BindEntityCollectionCastNode(EntityCollectionCastNode node)
  183. {
  184. IEdmEntityTypeReference entity = node.EntityItemType;
  185. Contract.Assert(entity != null, "NS casts can contain only entity types");
  186. Type clrType = EdmLibHelpers.GetClrType(entity, _model);
  187. Expression source = BindCastSourceNode(node.Source);
  188. return OfType(source, clrType);
  189. }
  190. private Expression BindCastSourceNode(QueryNode sourceNode)
  191. {
  192. Expression source;
  193. if (sourceNode == null)
  194. {
  195. // if the cast is on the root i.e $it (~/Products?$filter=NS.PopularProducts/.....),
  196. // source would be null. So bind null to '$it'.
  197. source = _lambdaParameters[ODataItParameterName];
  198. }
  199. else
  200. {
  201. source = Bind(sourceNode);
  202. }
  203. return source;
  204. }
  205. private static Expression OfType(Expression source, Type elementType)
  206. {
  207. Contract.Assert(source != null);
  208. Contract.Assert(elementType != null);
  209. if (IsIQueryable(source.Type))
  210. {
  211. return Expression.Call(null, ExpressionHelperMethods.QueryableOfType.MakeGenericMethod(elementType), source);
  212. }
  213. else
  214. {
  215. return Expression.Call(null, ExpressionHelperMethods.EnumerableOfType.MakeGenericMethod(elementType), source);
  216. }
  217. }
  218. private Expression BindNavigationPropertyNode(QueryNode sourceNode, IEdmNavigationProperty navigationProperty)
  219. {
  220. Expression source;
  221. // TODO: bug in uri parser is causing this property to be null for the root property.
  222. if (sourceNode == null)
  223. {
  224. source = _lambdaParameters[ODataItParameterName];
  225. }
  226. else
  227. {
  228. source = Bind(sourceNode);
  229. }
  230. return CreatePropertyAccessExpression(source, navigationProperty.Name);
  231. }
  232. private Expression BindBinaryOperatorNode(BinaryOperatorNode binaryOperatorNode)
  233. {
  234. Expression left = Bind(binaryOperatorNode.Left);
  235. Expression right = Bind(binaryOperatorNode.Right);
  236. // handle null propagation only if either of the operands can be null
  237. bool isNullPropagationRequired = _querySettings.HandleNullPropagation == HandleNullPropagationOption.True && (IsNullable(left.Type) || IsNullable(right.Type));
  238. if (isNullPropagationRequired)
  239. {
  240. // |----------------------------------------------------------------|
  241. // |SQL 3VL truth table. |
  242. // |----------------------------------------------------------------|
  243. // |p | q | p OR q | p AND q | p = q |
  244. // |----------------------------------------------------------------|
  245. // |True | True | True | True | True |
  246. // |True | False | True | False | False |
  247. // |True | NULL | True | NULL | NULL |
  248. // |False | True | True | False | False |
  249. // |False | False | False | False | True |
  250. // |False | NULL | NULL | False | NULL |
  251. // |NULL | True | True | NULL | NULL |
  252. // |NULL | False | NULL | False | NULL |
  253. // |NULL | NULL | Null | NULL | NULL |
  254. // |--------|-----------|---------------|---------------|-----------|
  255. // before we start with null propagation, convert the operators to nullable if already not.
  256. left = ToNullable(left);
  257. right = ToNullable(right);
  258. bool liftToNull = true;
  259. if (left == _nullConstant || right == _nullConstant)
  260. {
  261. liftToNull = false;
  262. }
  263. // Expression trees do a very good job of handling the 3VL truth table if we pass liftToNull true.
  264. return CreateBinaryExpression(binaryOperatorNode.OperatorKind, left, right, liftToNull: liftToNull);
  265. }
  266. else
  267. {
  268. return CreateBinaryExpression(binaryOperatorNode.OperatorKind, left, right, liftToNull: false);
  269. }
  270. }
  271. private Expression BindConstantNode(ConstantNode constantNode)
  272. {
  273. Contract.Assert(constantNode != null);
  274. // no need to parameterize null's as there cannot be multiple values for null.
  275. if (constantNode.Value == null)
  276. {
  277. return _nullConstant;
  278. }
  279. Type constantType = EdmLibHelpers.GetClrType(constantNode.TypeReference, _model, _assembliesResolver);
  280. if (_querySettings.EnableConstantParameterization)
  281. {
  282. return LinqParameterContainer.Parameterize(constantType, constantNode.Value);
  283. }
  284. else
  285. {
  286. return Expression.Constant(constantNode.Value, constantType);
  287. }
  288. }
  289. private Expression BindConvertNode(ConvertNode convertNode)
  290. {
  291. Contract.Assert(convertNode != null);
  292. Contract.Assert(convertNode.TypeReference != null);
  293. Expression source = Bind(convertNode.Source);
  294. Type conversionType = EdmLibHelpers.GetClrType(convertNode.TypeReference, _model, _assembliesResolver);
  295. if (conversionType == typeof(bool?) && source.Type == typeof(bool))
  296. {
  297. // we handle null propagation ourselves. So, if converting from bool to Nullable<bool> ignore.
  298. return source;
  299. }
  300. else if (source == _nullConstant)
  301. {
  302. return source;
  303. }
  304. else
  305. {
  306. Type sourceUnderlyingType = Nullable.GetUnderlyingType(source.Type) ?? source.Type;
  307. if (sourceUnderlyingType.IsEnum)
  308. {
  309. // we handle enum conversions ourselves
  310. return source;
  311. }
  312. else
  313. {
  314. // if a cast is from Nullable<T> to Non-Nullable<T> we need to check if source is null
  315. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True
  316. && IsNullable(source.Type) && !IsNullable(conversionType))
  317. {
  318. // source == null ? null : source.Value
  319. return
  320. Expression.Condition(
  321. test: CheckForNull(source),
  322. ifTrue: Expression.Constant(null, ToNullable(conversionType)),
  323. ifFalse: Expression.Convert(ExtractValueFromNullableExpression(source), ToNullable(conversionType)));
  324. }
  325. else
  326. {
  327. return Expression.Convert(source, conversionType);
  328. }
  329. }
  330. }
  331. }
  332. private LambdaExpression BindExpression(SingleValueNode expression, RangeVariable rangeVariable, Type elementType)
  333. {
  334. ParameterExpression filterParameter = Expression.Parameter(elementType, rangeVariable.Name);
  335. _lambdaParameters = new Dictionary<string, ParameterExpression>();
  336. _lambdaParameters.Add(rangeVariable.Name, filterParameter);
  337. Expression body = Bind(expression);
  338. return Expression.Lambda(body, filterParameter);
  339. }
  340. private Expression ApplyNullPropagationForFilterBody(Expression body)
  341. {
  342. if (IsNullable(body.Type))
  343. {
  344. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  345. {
  346. // handle null as false
  347. // body => body == true. passing liftToNull:false would convert null to false.
  348. body = Expression.Equal(body, Expression.Constant(true, typeof(bool?)), liftToNull: false, method: null);
  349. }
  350. else
  351. {
  352. body = Expression.Convert(body, typeof(bool));
  353. }
  354. }
  355. return body;
  356. }
  357. private Expression BindRangeVariable(RangeVariable rangeVariable)
  358. {
  359. ParameterExpression parameter = _lambdaParameters[rangeVariable.Name];
  360. return ConvertNonStandardPrimitives(parameter);
  361. }
  362. private Expression BindCollectionPropertyAccessNode(CollectionPropertyAccessNode propertyAccessNode)
  363. {
  364. Expression source = Bind(propertyAccessNode.Source);
  365. return CreatePropertyAccessExpression(source, propertyAccessNode.Property.Name);
  366. }
  367. private Expression BindPropertyAccessQueryNode(SingleValuePropertyAccessNode propertyAccessNode)
  368. {
  369. Expression source = Bind(propertyAccessNode.Source);
  370. return CreatePropertyAccessExpression(source, propertyAccessNode.Property.Name);
  371. }
  372. private Expression CreatePropertyAccessExpression(Expression source, string propertyName)
  373. {
  374. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type) && source != _lambdaParameters[ODataItParameterName])
  375. {
  376. Expression propertyAccessExpression = Expression.Property(RemoveInnerNullPropagation(source), propertyName);
  377. // source.property => source == null ? null : [CastToNullable]RemoveInnerNullPropagation(source).property
  378. // Notice that we are checking if source is null already. so we can safely remove any null checks when doing source.Property
  379. Expression ifFalse = ToNullable(ConvertNonStandardPrimitives(propertyAccessExpression));
  380. return
  381. Expression.Condition(
  382. test: Expression.Equal(source, _nullConstant),
  383. ifTrue: Expression.Constant(null, ifFalse.Type),
  384. ifFalse: ifFalse);
  385. }
  386. else
  387. {
  388. return ConvertNonStandardPrimitives(Expression.Property(source, propertyName));
  389. }
  390. }
  391. private Expression BindUnaryOperatorNode(UnaryOperatorNode unaryOperatorNode)
  392. {
  393. // No need to handle null-propagation here as CLR already handles it.
  394. // !(null) = null
  395. // -(null) = null
  396. Expression inner = Bind(unaryOperatorNode.Operand);
  397. switch (unaryOperatorNode.OperatorKind)
  398. {
  399. case UnaryOperatorKind.Negate:
  400. return Expression.Negate(inner);
  401. case UnaryOperatorKind.Not:
  402. return Expression.Not(inner);
  403. default:
  404. throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, unaryOperatorNode.Kind, typeof(FilterBinder).Name);
  405. }
  406. }
  407. private Expression BindSingleValueFunctionCallNode(SingleValueFunctionCallNode node)
  408. {
  409. switch (node.Name)
  410. {
  411. case ClrCanonicalFunctions.StartswithFunctionName:
  412. return BindStartsWith(node);
  413. case ClrCanonicalFunctions.EndswithFunctionName:
  414. return BindEndsWith(node);
  415. case ClrCanonicalFunctions.SubstringofFunctionName:
  416. return BindSubstringOf(node);
  417. case ClrCanonicalFunctions.SubstringFunctionName:
  418. return BindSubstring(node);
  419. case ClrCanonicalFunctions.LengthFunctionName:
  420. return BindLength(node);
  421. case ClrCanonicalFunctions.IndexofFunctionName:
  422. return BindIndexOf(node);
  423. case ClrCanonicalFunctions.TolowerFunctionName:
  424. return BindToLower(node);
  425. case ClrCanonicalFunctions.ToupperFunctionName:
  426. return BindToUpper(node);
  427. case ClrCanonicalFunctions.TrimFunctionName:
  428. return BindTrim(node);
  429. case ClrCanonicalFunctions.ConcatFunctionName:
  430. return BindConcat(node);
  431. case ClrCanonicalFunctions.YearFunctionName:
  432. case ClrCanonicalFunctions.MonthFunctionName:
  433. case ClrCanonicalFunctions.DayFunctionName:
  434. case ClrCanonicalFunctions.HourFunctionName:
  435. case ClrCanonicalFunctions.MinuteFunctionName:
  436. case ClrCanonicalFunctions.SecondFunctionName:
  437. return BindDateOrDateTimeOffsetProperty(node);
  438. case ClrCanonicalFunctions.YearsFunctionName:
  439. case ClrCanonicalFunctions.MonthsFunctionName:
  440. case ClrCanonicalFunctions.DaysFunctionName:
  441. case ClrCanonicalFunctions.HoursFunctionName:
  442. case ClrCanonicalFunctions.MinutesFunctionName:
  443. case ClrCanonicalFunctions.SecondsFunctionName:
  444. return BindTimeSpanProperty(node);
  445. case ClrCanonicalFunctions.RoundFunctionName:
  446. return BindRound(node);
  447. case ClrCanonicalFunctions.FloorFunctionName:
  448. return BindFloor(node);
  449. case ClrCanonicalFunctions.CeilingFunctionName:
  450. return BindCeiling(node);
  451. default:
  452. throw new NotImplementedException(Error.Format(SRResources.ODataFunctionNotSupported, node.Name));
  453. }
  454. }
  455. private Expression CreateFunctionCallWithNullPropagation(Expression functionCall, Expression[] arguments)
  456. {
  457. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  458. {
  459. Expression test = CheckIfArgumentsAreNull(arguments);
  460. if (test == _falseConstant)
  461. {
  462. // none of the arguments are/can be null.
  463. // so no need to do any null propagation
  464. return functionCall;
  465. }
  466. else
  467. {
  468. // if one of the arguments is null, result is null (not defined)
  469. return
  470. Expression.Condition(
  471. test: test,
  472. ifTrue: Expression.Constant(null, ToNullable(functionCall.Type)),
  473. ifFalse: ToNullable(functionCall));
  474. }
  475. }
  476. else
  477. {
  478. return functionCall;
  479. }
  480. }
  481. // we don't have to do null checks inside the function for arguments as we do the null checks before calling
  482. // the function when null propagation is enabled.
  483. // this method converts back "arg == null ? null : convert(arg)" to "arg"
  484. // Also, note that we can do this generically only because none of the odata functions that we support can take null
  485. // as an argument.
  486. private Expression RemoveInnerNullPropagation(Expression expression)
  487. {
  488. Contract.Assert(expression != null);
  489. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  490. {
  491. // only null propagation generates conditional expressions
  492. if (expression.NodeType == ExpressionType.Conditional)
  493. {
  494. expression = (expression as ConditionalExpression).IfFalse;
  495. Contract.Assert(expression != null);
  496. if (expression.NodeType == ExpressionType.Convert)
  497. {
  498. UnaryExpression unaryExpression = expression as UnaryExpression;
  499. Contract.Assert(unaryExpression != null);
  500. if (Nullable.GetUnderlyingType(unaryExpression.Type) == unaryExpression.Operand.Type)
  501. {
  502. // this is a cast from T to Nullable<T> which is redundant.
  503. expression = unaryExpression.Operand;
  504. }
  505. }
  506. }
  507. }
  508. return expression;
  509. }
  510. // creates an expression for the corresponding OData function.
  511. private Expression MakeFunctionCall(MemberInfo member, params Expression[] arguments)
  512. {
  513. Contract.Assert(member.MemberType == MemberTypes.Property || member.MemberType == MemberTypes.Method);
  514. IEnumerable<Expression> functionCallArguments = arguments;
  515. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  516. {
  517. // we don't have to check if the argument is null inside the function call as we do it already
  518. // before calling the function. So remove the redunadant null checks.
  519. functionCallArguments = arguments.Select(a => RemoveInnerNullPropagation(a));
  520. }
  521. // if the argument is of type Nullable<T>, then translate the argument to Nullable<T>.Value as none
  522. // of the cannonical functions have overloads for Nullable<> arguments.
  523. functionCallArguments = ExtractValueFromNullableArguments(functionCallArguments);
  524. Expression functionCall;
  525. if (member.MemberType == MemberTypes.Method)
  526. {
  527. MethodInfo method = member as MethodInfo;
  528. if (method.IsStatic)
  529. {
  530. functionCall = Expression.Call(null, method, functionCallArguments);
  531. }
  532. else
  533. {
  534. functionCall = Expression.Call(functionCallArguments.First(), method, functionCallArguments.Skip(1));
  535. }
  536. }
  537. else
  538. {
  539. // property
  540. functionCall = Expression.Property(functionCallArguments.First(), member as PropertyInfo);
  541. }
  542. return CreateFunctionCallWithNullPropagation(functionCall, arguments);
  543. }
  544. private Expression BindCeiling(SingleValueFunctionCallNode node)
  545. {
  546. Contract.Assert("ceiling" == node.Name);
  547. Expression[] arguments = BindArguments(node.Arguments);
  548. Contract.Assert(arguments.Length == 1 && IsDoubleOrDecimal(arguments[0].Type));
  549. MethodInfo ceiling = arguments[0].Type == typeof(double) ? ClrCanonicalFunctions.CeilingOfDouble : ClrCanonicalFunctions.CeilingOfDecimal;
  550. return MakeFunctionCall(ceiling, arguments);
  551. }
  552. private Expression BindFloor(SingleValueFunctionCallNode node)
  553. {
  554. Contract.Assert("floor" == node.Name);
  555. Expression[] arguments = BindArguments(node.Arguments);
  556. Contract.Assert(arguments.Length == 1 && IsDoubleOrDecimal(arguments[0].Type));
  557. MethodInfo floor = arguments[0].Type == typeof(double) ? ClrCanonicalFunctions.FloorOfDouble : ClrCanonicalFunctions.FloorOfDecimal;
  558. return MakeFunctionCall(floor, arguments);
  559. }
  560. private Expression BindRound(SingleValueFunctionCallNode node)
  561. {
  562. Contract.Assert("round" == node.Name);
  563. Expression[] arguments = BindArguments(node.Arguments);
  564. Contract.Assert(arguments.Length == 1 && IsDoubleOrDecimal(arguments[0].Type));
  565. MethodInfo round = arguments[0].Type == typeof(double) ? ClrCanonicalFunctions.RoundOfDouble : ClrCanonicalFunctions.RoundOfDecimal;
  566. return MakeFunctionCall(round, arguments);
  567. }
  568. private Expression BindDateOrDateTimeOffsetProperty(SingleValueFunctionCallNode node)
  569. {
  570. Expression[] arguments = BindArguments(node.Arguments);
  571. Contract.Assert(arguments.Length == 1 && IsDateOrOffset(arguments[0].Type));
  572. PropertyInfo property;
  573. if (IsDate(arguments[0].Type))
  574. {
  575. Contract.Assert(ClrCanonicalFunctions.DateProperties.ContainsKey(node.Name));
  576. property = ClrCanonicalFunctions.DateProperties[node.Name];
  577. }
  578. else
  579. {
  580. Contract.Assert(ClrCanonicalFunctions.DateTimeOffsetProperties.ContainsKey(node.Name));
  581. property = ClrCanonicalFunctions.DateTimeOffsetProperties[node.Name];
  582. }
  583. return MakeFunctionCall(property, arguments);
  584. }
  585. private Expression BindTimeSpanProperty(SingleValueFunctionCallNode node)
  586. {
  587. Expression[] arguments = BindArguments(node.Arguments);
  588. Contract.Assert(arguments.Length == 1 && IsDateOrOffset(arguments[0].Type));
  589. Contract.Assert(ClrCanonicalFunctions.TimeSpanProperties.ContainsKey(node.Name));
  590. return MakeFunctionCall(ClrCanonicalFunctions.TimeSpanProperties[node.Name], arguments);
  591. }
  592. private Expression BindConcat(SingleValueFunctionCallNode node)
  593. {
  594. Contract.Assert("concat" == node.Name);
  595. Expression[] arguments = BindArguments(node.Arguments);
  596. ValidateAllStringArguments(node.Name, arguments);
  597. Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
  598. return MakeFunctionCall(ClrCanonicalFunctions.Concat, arguments);
  599. }
  600. private Expression BindTrim(SingleValueFunctionCallNode node)
  601. {
  602. Contract.Assert("trim" == node.Name);
  603. Expression[] arguments = BindArguments(node.Arguments);
  604. ValidateAllStringArguments(node.Name, arguments);
  605. Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
  606. return MakeFunctionCall(ClrCanonicalFunctions.Trim, arguments);
  607. }
  608. private Expression BindToUpper(SingleValueFunctionCallNode node)
  609. {
  610. Contract.Assert("toupper" == node.Name);
  611. Expression[] arguments = BindArguments(node.Arguments);
  612. ValidateAllStringArguments(node.Name, arguments);
  613. Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
  614. return MakeFunctionCall(ClrCanonicalFunctions.ToUpper, arguments);
  615. }
  616. private Expression BindToLower(SingleValueFunctionCallNode node)
  617. {
  618. Contract.Assert("tolower" == node.Name);
  619. Expression[] arguments = BindArguments(node.Arguments);
  620. ValidateAllStringArguments(node.Name, arguments);
  621. Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
  622. return MakeFunctionCall(ClrCanonicalFunctions.ToLower, arguments);
  623. }
  624. private Expression BindIndexOf(SingleValueFunctionCallNode node)
  625. {
  626. Contract.Assert("indexof" == node.Name);
  627. Expression[] arguments = BindArguments(node.Arguments);
  628. ValidateAllStringArguments(node.Name, arguments);
  629. Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
  630. return MakeFunctionCall(ClrCanonicalFunctions.IndexOf, arguments);
  631. }
  632. private Expression BindSubstring(SingleValueFunctionCallNode node)
  633. {
  634. Contract.Assert("substring" == node.Name);
  635. Expression[] arguments = BindArguments(node.Arguments);
  636. if (arguments[0].Type != typeof(string))
  637. {
  638. throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, node.Name));
  639. }
  640. Expression functionCall;
  641. if (arguments.Length == 2)
  642. {
  643. Contract.Assert(IsInteger(arguments[1].Type));
  644. // When null propagation is allowed, we use a safe version of String.Substring(int).
  645. // But for providers that would not recognize custom expressions like this, we map
  646. // directly to String.Substring(int)
  647. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  648. {
  649. // Safe function is static and takes string "this" as first argument
  650. functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartNoThrow, arguments);
  651. }
  652. else
  653. {
  654. functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStart, arguments);
  655. }
  656. }
  657. else
  658. {
  659. // arguments.Length == 3 implies String.Substring(int, int)
  660. Contract.Assert(arguments.Length == 3 && IsInteger(arguments[1].Type) && IsInteger(arguments[2].Type));
  661. // When null propagation is allowed, we use a safe version of String.Substring(int, int).
  662. // But for providers that would not recognize custom expressions like this, we map
  663. // directly to String.Substring(int, int)
  664. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True)
  665. {
  666. // Safe function is static and takes string "this" as first argument
  667. functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartAndLengthNoThrow, arguments);
  668. }
  669. else
  670. {
  671. functionCall = MakeFunctionCall(ClrCanonicalFunctions.SubstringStartAndLength, arguments);
  672. }
  673. }
  674. return functionCall;
  675. }
  676. private Expression BindLength(SingleValueFunctionCallNode node)
  677. {
  678. Contract.Assert("length" == node.Name);
  679. Expression[] arguments = BindArguments(node.Arguments);
  680. ValidateAllStringArguments(node.Name, arguments);
  681. Contract.Assert(arguments.Length == 1 && arguments[0].Type == typeof(string));
  682. return MakeFunctionCall(ClrCanonicalFunctions.Length, arguments);
  683. }
  684. private Expression BindSubstringOf(SingleValueFunctionCallNode node)
  685. {
  686. Contract.Assert("substringof" == node.Name);
  687. Expression[] arguments = BindArguments(node.Arguments);
  688. ValidateAllStringArguments(node.Name, arguments);
  689. Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
  690. // NOTE: this is reversed because it is reverse in WCF DS and in the OData spec
  691. return MakeFunctionCall(ClrCanonicalFunctions.Contains, arguments[1], arguments[0]);
  692. }
  693. private Expression BindStartsWith(SingleValueFunctionCallNode node)
  694. {
  695. Contract.Assert("startswith" == node.Name);
  696. Expression[] arguments = BindArguments(node.Arguments);
  697. ValidateAllStringArguments(node.Name, arguments);
  698. Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
  699. return MakeFunctionCall(ClrCanonicalFunctions.StartsWith, arguments);
  700. }
  701. private Expression BindEndsWith(SingleValueFunctionCallNode node)
  702. {
  703. Contract.Assert("endswith" == node.Name);
  704. Expression[] arguments = BindArguments(node.Arguments);
  705. ValidateAllStringArguments(node.Name, arguments);
  706. Contract.Assert(arguments.Length == 2 && arguments[0].Type == typeof(string) && arguments[1].Type == typeof(string));
  707. return MakeFunctionCall(ClrCanonicalFunctions.EndsWith, arguments);
  708. }
  709. private Expression[] BindArguments(IEnumerable<QueryNode> nodes)
  710. {
  711. return nodes.OfType<SingleValueNode>().Select(n => Bind(n)).ToArray();
  712. }
  713. private static void ValidateAllStringArguments(string functionName, Expression[] arguments)
  714. {
  715. if (arguments.Any(arg => arg.Type != typeof(string)))
  716. {
  717. throw new ODataException(Error.Format(SRResources.FunctionNotSupportedOnEnum, functionName));
  718. }
  719. }
  720. private Expression BindAllNode(AllNode allNode)
  721. {
  722. ParameterExpression allIt = HandleLambdaParameters(allNode.RangeVariables);
  723. Expression source;
  724. Contract.Assert(allNode.Source != null);
  725. source = Bind(allNode.Source);
  726. Expression body = source;
  727. Contract.Assert(allNode.Body != null);
  728. body = Bind(allNode.Body);
  729. body = ApplyNullPropagationForFilterBody(body);
  730. body = Expression.Lambda(body, allIt);
  731. Expression all = All(source, body);
  732. ExitLamdbaScope();
  733. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type))
  734. {
  735. // IFF(source == null) null; else Any(body);
  736. all = ToNullable(all);
  737. return Expression.Condition(
  738. test: Expression.Equal(source, _nullConstant),
  739. ifTrue: Expression.Constant(null, all.Type),
  740. ifFalse: all);
  741. }
  742. else
  743. {
  744. return all;
  745. }
  746. }
  747. private Expression BindAnyNode(AnyNode anyNode)
  748. {
  749. ParameterExpression anyIt = HandleLambdaParameters(anyNode.RangeVariables);
  750. Expression source;
  751. Contract.Assert(anyNode.Source != null);
  752. source = Bind(anyNode.Source);
  753. Expression body = null;
  754. // uri parser places an Constant node with value true for empty any() body
  755. if (anyNode.Body != null && anyNode.Body.Kind != QueryNodeKind.Constant)
  756. {
  757. body = Bind(anyNode.Body);
  758. body = ApplyNullPropagationForFilterBody(body);
  759. body = Expression.Lambda(body, anyIt);
  760. }
  761. Expression any = Any(source, body);
  762. ExitLamdbaScope();
  763. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type))
  764. {
  765. // IFF(source == null) null; else Any(body);
  766. any = ToNullable(any);
  767. return Expression.Condition(
  768. test: Expression.Equal(source, _nullConstant),
  769. ifTrue: Expression.Constant(null, any.Type),
  770. ifFalse: any);
  771. }
  772. else
  773. {
  774. return any;
  775. }
  776. }
  777. private ParameterExpression HandleLambdaParameters(IEnumerable<RangeVariable> rangeVariables)
  778. {
  779. ParameterExpression lambdaIt = null;
  780. EnterLambdaScope();
  781. Dictionary<string, ParameterExpression> newParameters = new Dictionary<string, ParameterExpression>();
  782. foreach (RangeVariable rangeVariable in rangeVariables)
  783. {
  784. ParameterExpression parameter;
  785. if (!_lambdaParameters.TryGetValue(rangeVariable.Name, out parameter))
  786. {
  787. // Work-around issue 481323 where UriParser yields a collection parameter type
  788. // for primitive collections rather than the inner element type of the collection.
  789. // Remove this block of code when 481323 is resolved.
  790. IEdmTypeReference edmTypeReference = rangeVariable.TypeReference;
  791. IEdmCollectionTypeReference collectionTypeReference = edmTypeReference as IEdmCollectionTypeReference;
  792. if (collectionTypeReference != null)
  793. {
  794. IEdmCollectionType collectionType = collectionTypeReference.Definition as IEdmCollectionType;
  795. if (collectionType != null)
  796. {
  797. edmTypeReference = collectionType.ElementType;
  798. }
  799. }
  800. parameter = Expression.Parameter(EdmLibHelpers.GetClrType(edmTypeReference, _model, _assembliesResolver), rangeVariable.Name);
  801. Contract.Assert(lambdaIt == null, "There can be only one parameter in an Any/All lambda");
  802. lambdaIt = parameter;
  803. }
  804. newParameters.Add(rangeVariable.Name, parameter);
  805. }
  806. _lambdaParameters = newParameters;
  807. return lambdaIt;
  808. }
  809. // If the expression is of non-standard edm primitive type (like uint), convert the expression to its standard edm type.
  810. // Also, note that only expressions generated for ushort, uint and ulong can be understood by linq2sql and EF.
  811. // The rest (char, char[], Binary) would cause issues with linq2sql and EF.
  812. private Expression ConvertNonStandardPrimitives(Expression source)
  813. {
  814. bool isNonstandardEdmPrimitive;
  815. Type conversionType = EdmLibHelpers.IsNonstandardEdmPrimitive(source.Type, out isNonstandardEdmPrimitive);
  816. if (isNonstandardEdmPrimitive)
  817. {
  818. Type sourceType = source.Type;
  819. sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
  820. Contract.Assert(sourceType != conversionType);
  821. Expression convertedExpression = null;
  822. if (sourceType.IsEnum)
  823. {
  824. // we handle enum conversions ourselves
  825. convertedExpression = source;
  826. }
  827. else
  828. {
  829. switch (Type.GetTypeCode(sourceType))
  830. {
  831. case TypeCode.UInt16:
  832. case TypeCode.UInt32:
  833. case TypeCode.UInt64:
  834. convertedExpression = Expression.Convert(ExtractValueFromNullableExpression(source), conversionType);
  835. break;
  836. case TypeCode.Char:
  837. convertedExpression = Expression.Call(ExtractValueFromNullableExpression(source), "ToString", typeArguments: null, arguments: null);
  838. break;
  839. case TypeCode.Object:
  840. if (sourceType == typeof(char[]))
  841. {
  842. convertedExpression = Expression.New(typeof(string).GetConstructor(new[] { typeof(char[]) }), source);
  843. }
  844. else if (sourceType == typeof(XElement))
  845. {
  846. convertedExpression = Expression.Call(source, "ToString", typeArguments: null, arguments: null);
  847. }
  848. else if (sourceType == typeof(Binary))
  849. {
  850. convertedExpression = Expression.Call(source, "ToArray", typeArguments: null, arguments: null);
  851. }
  852. break;
  853. default:
  854. Contract.Assert(false, Error.Format("missing non-standard type support for {0}", sourceType.Name));
  855. break;
  856. }
  857. }
  858. if (_querySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type))
  859. {
  860. // source == null ? null : source
  861. return Expression.Condition(
  862. CheckForNull(source),
  863. ifTrue: Expression.Constant(null, ToNullable(convertedExpression.Type)),
  864. ifFalse: ToNullable(convertedExpression));
  865. }
  866. else
  867. {
  868. return convertedExpression;
  869. }
  870. }
  871. return source;
  872. }
  873. private void EnterLambdaScope()
  874. {
  875. Contract.Assert(_lambdaParameters != null);
  876. _parametersStack.Push(_lambdaParameters);
  877. }
  878. private void ExitLamdbaScope()
  879. {
  880. if (_parametersStack.Count != 0)
  881. {
  882. _lambdaParameters = _parametersStack.Pop();
  883. }
  884. else
  885. {
  886. _lambdaParameters = null;
  887. }
  888. }
  889. private static Expression CreateBinaryExpression(BinaryOperatorKind binaryOperator, Expression left, Expression right, bool liftToNull)
  890. {
  891. ExpressionType binaryExpressionType;
  892. // When comparing an enum to a string, parse the string, convert both to the enum underlying type, and compare the values
  893. // When comparing an enum to an enum, convert both to the underlying type, and compare the values
  894. Type leftUnderlyingType = Nullable.GetUnderlyingType(left.Type) ?? left.Type;
  895. Type rightUnderlyingType = Nullable.GetUnderlyingType(right.Type) ?? right.Type;
  896. if (leftUnderlyingType.IsEnum || rightUnderlyingType.IsEnum)
  897. {
  898. Type enumType = leftUnderlyingType.IsEnum ? leftUnderlyingType : rightUnderlyingType;
  899. Type enumUnderlyingType = Enum.GetUnderlyingType(enumType);
  900. left = ConvertToEnumUnderlyingType(left, enumType, enumUnderlyingType);
  901. right = ConvertToEnumUnderlyingType(right, enumType, enumUnderlyingType);
  902. }
  903. if (left.Type != right.Type)
  904. {
  905. // one of them must be nullable and the other is not.
  906. left = ToNullable(left);
  907. right = ToNullable(right);
  908. }
  909. if (left.Type == typeof(string) || right.Type == typeof(string))
  910. {
  911. // convert nulls of type object to nulls of type string to make the String.Compare call work
  912. left = ConvertNull(left, typeof(string));
  913. right = ConvertNull(right, typeof(string));
  914. // Use string.Compare instead of comparison for gt, ge, lt, le between two strings since direct comparisons are not supported
  915. switch (binaryOperator)
  916. {
  917. case BinaryOperatorKind.GreaterThan:
  918. case BinaryOperatorKind.GreaterThanOrEqual:
  919. case BinaryOperatorKind.LessThan:
  920. case BinaryOperatorKind.LessThanOrEqual:
  921. left = Expression.Call(_stringCompareMethodInfo, left, right, _ordinalStringComparisonConstant);
  922. right = _zeroConstant;
  923. break;
  924. default:
  925. break;
  926. }
  927. }
  928. if (_binaryOperatorMapping.TryGetValue(binaryOperator, out binaryExpressionType))
  929. {
  930. if (left.Type == typeof(byte[]) || right.Type == typeof(byte[]))
  931. {
  932. left = ConvertNull(left, typeof(byte[]));
  933. right = ConvertNull(right, typeof(byte[]));
  934. switch (binaryExpressionType)
  935. {

Large files files are truncated, but you can click here to view the full file