PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

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

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

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