PageRenderTime 70ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://aspnetwebstack.codeplex.com
C# | 1357 lines | 1043 code | 230 blank | 84 comment | 274 complexity | d70cce01dfceda3e3087e70ec8ebf260 MD5 | raw file

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

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