PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/Sugar/Implementation/ExpressionDispatcher.Analyzer.cs

https://github.com/ekovalenko-softheme/mono
C# | 1648 lines | 1160 code | 152 blank | 336 comment | 245 complexity | 70c18b9b0029d1d7d5df93da8b885d06 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Unlicense
  1. #region MIT license
  2. //
  3. // MIT license
  4. //
  5. // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. //
  25. #endregion
  26. using System;
  27. using System.Collections;
  28. using System.Collections.Generic;
  29. using System.Linq;
  30. using System.Linq.Expressions;
  31. using System.Reflection;
  32. #if MONO_STRICT
  33. using System.Data.Linq;
  34. #else
  35. using DbLinq.Data.Linq;
  36. #endif
  37. using DbLinq.Data.Linq.Implementation;
  38. using DbLinq.Data.Linq.Sugar;
  39. using DbLinq.Data.Linq.Sugar.ExpressionMutator;
  40. using DbLinq.Data.Linq.Sugar.Expressions;
  41. using DbLinq.Data.Linq.Sugar.Implementation;
  42. using DbLinq.Factory;
  43. using DbLinq.Util;
  44. namespace DbLinq.Data.Linq.Sugar.Implementation
  45. {
  46. partial class ExpressionDispatcher
  47. {
  48. public Expression Analyze(ExpressionChain expressions, Expression parameter, BuilderContext builderContext)
  49. {
  50. Expression tableExpression = parameter;
  51. Expression last = expressions.Last();
  52. IExpressionLanguageParser languageParser = ObjectFactory.Get<IExpressionLanguageParser>();
  53. foreach (Expression e in expressions)
  54. {
  55. if (e == last)
  56. builderContext.IsExternalInExpressionChain = true;
  57. // write full debug
  58. #if DEBUG && !MONO_STRICT
  59. var log = builderContext.QueryContext.DataContext.Log;
  60. if (log != null)
  61. log.WriteExpression(e);
  62. #endif
  63. // Convert linq Expressions to QueryOperationExpressions and QueryConstantExpressions
  64. // Query expressions language identification
  65. var currentExpression = languageParser.Parse(e, builderContext);
  66. // Query expressions query identification
  67. currentExpression = this.Analyze(currentExpression, tableExpression, builderContext);
  68. if (!builderContext.IsExternalInExpressionChain)
  69. {
  70. EntitySetExpression setExpression = currentExpression as EntitySetExpression;
  71. if (setExpression != null)
  72. currentExpression = setExpression.TableExpression;
  73. }
  74. tableExpression = currentExpression;
  75. }
  76. return tableExpression;
  77. }
  78. /// <summary>
  79. /// Entry point for Analyzis
  80. /// </summary>
  81. /// <param name="expression"></param>
  82. /// <param name="parameter"></param>
  83. /// <param name="builderContext"></param>
  84. /// <returns></returns>
  85. protected virtual Expression Analyze(Expression expression, Expression parameter, BuilderContext builderContext)
  86. {
  87. return Analyze(expression, new[] { parameter }, builderContext);
  88. }
  89. protected virtual Expression Analyze(Expression expression, BuilderContext builderContext)
  90. {
  91. return Analyze(expression, new Expression[0], builderContext);
  92. }
  93. protected virtual Expression Analyze(Expression expression, IList<Expression> parameters, BuilderContext builderContext)
  94. {
  95. switch (expression.NodeType)
  96. {
  97. case ExpressionType.Call:
  98. return AnalyzeCall((MethodCallExpression)expression, parameters, builderContext);
  99. case ExpressionType.Lambda:
  100. return AnalyzeLambda(expression, parameters, builderContext);
  101. case ExpressionType.Parameter:
  102. return AnalyzeParameter(expression, builderContext);
  103. case ExpressionType.Quote:
  104. return AnalyzeQuote(expression, parameters, builderContext);
  105. case ExpressionType.MemberAccess:
  106. return AnalyzeMember(expression, builderContext);
  107. #region case ExpressionType.<Common operators>:
  108. case ExpressionType.Add:
  109. case ExpressionType.AddChecked:
  110. case ExpressionType.Divide:
  111. case ExpressionType.Modulo:
  112. case ExpressionType.Multiply:
  113. case ExpressionType.MultiplyChecked:
  114. case ExpressionType.Power:
  115. case ExpressionType.Subtract:
  116. case ExpressionType.SubtractChecked:
  117. case ExpressionType.And:
  118. case ExpressionType.Or:
  119. case ExpressionType.ExclusiveOr:
  120. case ExpressionType.LeftShift:
  121. case ExpressionType.RightShift:
  122. case ExpressionType.AndAlso:
  123. case ExpressionType.OrElse:
  124. case ExpressionType.Equal:
  125. case ExpressionType.NotEqual:
  126. case ExpressionType.GreaterThanOrEqual:
  127. case ExpressionType.GreaterThan:
  128. case ExpressionType.LessThan:
  129. case ExpressionType.LessThanOrEqual:
  130. case ExpressionType.Coalesce:
  131. //case ExpressionType.ArrayIndex
  132. //case ExpressionType.ArrayLength
  133. case ExpressionType.Convert:
  134. case ExpressionType.ConvertChecked:
  135. case ExpressionType.Negate:
  136. case ExpressionType.NegateChecked:
  137. case ExpressionType.Not:
  138. //case ExpressionType.TypeAs
  139. case ExpressionType.UnaryPlus:
  140. case ExpressionType.MemberInit:
  141. #endregion
  142. return AnalyzeOperator(expression, builderContext);
  143. case ExpressionType.New:
  144. return AnalyzeNewOperator(expression, builderContext);
  145. case ExpressionType.Constant:
  146. return AnalyzeConstant(expression, builderContext);
  147. case ExpressionType.Invoke:
  148. return AnalyzeInvoke(expression, parameters, builderContext);
  149. }
  150. return expression;
  151. }
  152. /// <summary>
  153. /// Analyzes method call, uses specified parameters
  154. /// </summary>
  155. /// <param name="expression"></param>
  156. /// <param name="parameters"></param>
  157. /// <param name="builderContext"></param>
  158. /// <returns></returns>
  159. protected virtual Expression AnalyzeCall(MethodCallExpression expression, IList<Expression> parameters, BuilderContext builderContext)
  160. {
  161. var operands = expression.GetOperands().ToList();
  162. var operarandsToSkip = expression.Method.IsStatic ? 1 : 0;
  163. var originalParameters = operands.Skip(parameters.Count + operarandsToSkip);
  164. var newParameters = parameters.Union(originalParameters).ToList();
  165. return AnalyzeQueryableCall(expression.Method, newParameters, builderContext) ??
  166. AnalyzeStringCall(expression.Method, newParameters, builderContext) ??
  167. AnalyzeMathCall(expression.Method, newParameters, builderContext) ??
  168. AnalyzeUnknownCall(expression, newParameters, builderContext);
  169. }
  170. private Expression AnalyzeQueryableCall(MethodInfo method, IList<Expression> parameters, BuilderContext builderContext)
  171. {
  172. if (!(method.DeclaringType == typeof(Queryable) || method.DeclaringType == typeof(Enumerable)))
  173. return null;
  174. var popCallStack = PushCallStack(method, builderContext);
  175. // all methods to handle are listed here:
  176. // ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system.core/html/2a54ce9d-76f2-81e2-95bb-59740c85386b.htm
  177. string methodName = method.Name;
  178. switch (methodName)
  179. {
  180. case "All":
  181. return popCallStack(AnalyzeAll(parameters, builderContext));
  182. case "Any":
  183. return popCallStack(AnalyzeAny(parameters, builderContext));
  184. case "Average":
  185. return popCallStack(AnalyzeProjectionQuery(SpecialExpressionType.Average, parameters, builderContext));
  186. case "Concat":
  187. return popCallStack(AnalyzeSelectOperation(SelectOperatorType.UnionAll, parameters, builderContext));
  188. case "Contains":
  189. return popCallStack(AnalyzeContains(parameters, builderContext));
  190. case "Count":
  191. return popCallStack(AnalyzeProjectionQuery(SpecialExpressionType.Count, parameters, builderContext));
  192. case "DefaultIfEmpty":
  193. return popCallStack(AnalyzeOuterJoin(parameters, builderContext));
  194. case "Distinct":
  195. return popCallStack(AnalyzeDistinct(parameters, builderContext));
  196. case "Except":
  197. return popCallStack(AnalyzeSelectOperation(SelectOperatorType.Exception, parameters, builderContext));
  198. case "First":
  199. case "FirstOrDefault":
  200. return popCallStack(AnalyzeScalar(methodName, 1, parameters, builderContext));
  201. case "GroupBy":
  202. return popCallStack(AnalyzeGroupBy(parameters, builderContext));
  203. case "GroupJoin":
  204. return popCallStack(AnalyzeGroupJoin(parameters, builderContext));
  205. case "Intersect":
  206. return popCallStack(AnalyzeSelectOperation(SelectOperatorType.Intersection, parameters, builderContext));
  207. case "Join":
  208. return popCallStack(AnalyzeJoin(parameters, builderContext));
  209. case "Last":
  210. return popCallStack(AnalyzeScalar(methodName, null, parameters, builderContext));
  211. case "Max":
  212. return popCallStack(AnalyzeProjectionQuery(SpecialExpressionType.Max, parameters, builderContext));
  213. case "Min":
  214. return popCallStack(AnalyzeProjectionQuery(SpecialExpressionType.Min, parameters, builderContext));
  215. case "OrderBy":
  216. case "ThenBy":
  217. return popCallStack(AnalyzeOrderBy(parameters, false, builderContext));
  218. case "OrderByDescending":
  219. case "ThenByDescending":
  220. return popCallStack(AnalyzeOrderBy(parameters, true, builderContext));
  221. case "Select":
  222. return popCallStack(AnalyzeSelect(parameters, builderContext));
  223. case "SelectMany":
  224. return popCallStack(AnalyzeSelectMany(parameters, builderContext));
  225. case "Single":
  226. case "SingleOrDefault":
  227. return popCallStack(AnalyzeScalar(methodName, 2, parameters, builderContext));
  228. case "Skip":
  229. return popCallStack(AnalyzeSkip(parameters, builderContext));
  230. case "Sum":
  231. return popCallStack(AnalyzeProjectionQuery(SpecialExpressionType.Sum, parameters, builderContext));
  232. case "Take":
  233. return popCallStack(AnalyzeTake(parameters, builderContext));
  234. case "Union":
  235. return popCallStack(AnalyzeSelectOperation(SelectOperatorType.Union, parameters, builderContext));
  236. case "Where":
  237. return popCallStack(AnalyzeWhere(parameters, builderContext));
  238. default:
  239. if (method.DeclaringType == typeof(Queryable))
  240. throw Error.BadArgument("S0133: Implement QueryMethod Queryable.{0}.", methodName);
  241. return popCallStack(null);
  242. }
  243. }
  244. Func<Expression, Expression> PushCallStack(MethodInfo method, BuilderContext builderContext)
  245. {
  246. builderContext.CallStack.Push(method);
  247. Func<Expression, Expression> popCallStack = r =>
  248. {
  249. builderContext.CallStack.Pop();
  250. return r;
  251. };
  252. return popCallStack;
  253. }
  254. private Expression AnalyzeStringCall(MethodInfo method, IList<Expression> parameters, BuilderContext builderContext)
  255. {
  256. if (method.DeclaringType != typeof(string))
  257. return null;
  258. var popCallStack = PushCallStack(method, builderContext);
  259. switch (method.Name)
  260. {
  261. case "Contains":
  262. return popCallStack(AnalyzeLike(parameters, builderContext));
  263. case "EndsWith":
  264. return popCallStack(AnalyzeLikeEnd(parameters, builderContext));
  265. case "IndexOf":
  266. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.IndexOf, parameters, builderContext));
  267. case "Insert":
  268. return popCallStack(AnalyzeStringInsert(parameters, builderContext));
  269. case "Remove":
  270. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.Remove, parameters, builderContext));
  271. case "Replace":
  272. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.Replace, parameters, builderContext));
  273. case "StartsWith":
  274. return popCallStack(AnalyzeLikeStart(parameters, builderContext));
  275. case "Substring":
  276. return popCallStack(AnalyzeSubString(parameters, builderContext));
  277. case "ToLower":
  278. return popCallStack(AnalyzeToLower(parameters, builderContext));
  279. case "ToString":
  280. return popCallStack(AnalyzeToString(method, parameters, builderContext));
  281. case "ToUpper":
  282. return popCallStack(AnalyzeToUpper(parameters, builderContext));
  283. case "Trim":
  284. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.Trim, parameters, builderContext));
  285. case "TrimEnd":
  286. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.RTrim, parameters, builderContext));
  287. case "TrimStart":
  288. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.LTrim, parameters, builderContext));
  289. default:
  290. throw Error.BadArgument("S0133: Implement QueryMethod String.{0}.", method.Name);
  291. }
  292. }
  293. private Expression AnalyzeMathCall(MethodInfo method, IList<Expression> parameters, BuilderContext builderContext)
  294. {
  295. if (method.DeclaringType != typeof(System.Math))
  296. return null;
  297. var popCallStack = PushCallStack(method, builderContext);
  298. switch (method.Name)
  299. {
  300. case "Abs":
  301. case "Exp":
  302. case "Floor":
  303. case "Pow":
  304. case "Round":
  305. case "Sign":
  306. case "Sqrt":
  307. return popCallStack(AnalyzeGenericSpecialExpressionType((SpecialExpressionType)Enum.Parse(typeof(SpecialExpressionType), method.Name), parameters, builderContext));
  308. case "Log":
  309. return popCallStack(AnalyzeLog(parameters, builderContext));
  310. case "Log10":
  311. return popCallStack(AnalyzeGenericSpecialExpressionType(SpecialExpressionType.Log, parameters, builderContext));
  312. default:
  313. throw Error.BadArgument("S0133: Implement QueryMethod Math.{0}.", method.Name);
  314. }
  315. }
  316. private Expression AnalyzeUnknownCall(MethodCallExpression expression, IList<Expression> parameters, BuilderContext builderContext)
  317. {
  318. var method = expression.Method;
  319. switch (method.Name)
  320. {
  321. case "Parse":
  322. if (method.IsStatic && parameters.Count == 1)
  323. return AnalyzeParse(method, parameters, builderContext);
  324. break;
  325. case "ToString": // Can we sanity check this type?
  326. return AnalyzeToString(method, parameters, builderContext);
  327. }
  328. var args = new List<Expression>();
  329. foreach (var arg in expression.Arguments)
  330. {
  331. Expression newArg = arg;
  332. var pe = arg as ParameterExpression;
  333. if (pe != null)
  334. {
  335. if (!builderContext.Parameters.TryGetValue(pe.Name, out newArg))
  336. throw new NotSupportedException("Do not currently support expression: " + expression);
  337. }
  338. else
  339. newArg = Analyze(arg, builderContext);
  340. args.Add(newArg);
  341. }
  342. return Expression.Call(expression.Object, expression.Method, args);
  343. }
  344. private Expression AnalyzeStringInsert(IList<Expression> parameters, BuilderContext builderContext)
  345. {
  346. var startIndexExpression = new StartIndexOffsetExpression(builderContext.QueryContext.DataContext.Vendor.SqlProvider.StringIndexStartsAtOne, parameters.ElementAt(1));
  347. var stringToInsertExpression = parameters.ElementAt(2);
  348. return AnalyzeGenericSpecialExpressionType(SpecialExpressionType.StringInsert, new Expression[] { parameters.First(), startIndexExpression, stringToInsertExpression }, builderContext);
  349. }
  350. protected virtual Expression AnalyzeLog(IList<Expression> parameters, BuilderContext builderContext)
  351. {
  352. if (parameters.Count == 1)
  353. return new SpecialExpression(SpecialExpressionType.Ln, parameters.Select(p => Analyze(p, builderContext)).ToList());
  354. else if (parameters.Count == 2)
  355. return new SpecialExpression(SpecialExpressionType.Log, parameters.Select(p => Analyze(p, builderContext)).ToList());
  356. else
  357. throw new NotSupportedException();
  358. }
  359. protected virtual Expression AnalyzeGenericSpecialExpressionType(SpecialExpressionType specialType, IList<Expression> parameters, BuilderContext builderContext)
  360. {
  361. return new SpecialExpression(specialType, parameters.Select(p => Analyze(p, builderContext)).ToList());
  362. }
  363. protected virtual Expression AnalyzeParse(MethodInfo method, IList<Expression> parameters, BuilderContext builderContext)
  364. {
  365. if (method.IsStatic && parameters.Count == 1)
  366. {
  367. Expression parsed = null;
  368. Expression toParse = Analyze(parameters.First(), builderContext);
  369. InputParameterExpression inputParameterToParse = toParse as InputParameterExpression;
  370. if (inputParameterToParse != null)
  371. {
  372. ExpressionTier tier = ExpressionQualifier.GetTier(parameters[0]);
  373. if (tier == ExpressionTier.Clr)
  374. {
  375. parsed = RegisterParameter(System.Linq.Expressions.Expression.Call(method, inputParameterToParse.Expression), inputParameterToParse.Alias, builderContext);
  376. UnregisterParameter(inputParameterToParse, builderContext);
  377. }
  378. }
  379. if(parsed == null)
  380. {
  381. parsed = Expression.Convert(toParse, method.ReturnType, method);
  382. ExpressionTier tier = ExpressionQualifier.GetTier(toParse);
  383. //pibgeus: I would like to call to the expression optimizer since the exception must be thrown if the expression cannot be executed
  384. //in Clr tier, if it can be executed in Clr tier it should continue
  385. // ie: from e in db.Employees where DateTime.Parse("1/1/1999").Year==1999 select e <--- this should work
  386. // ie: from e in db.Employees where DateTime.Parse(e.BirthDate).Year==1999 select e <--- a NotSupportedException must be throwed (this is the behaviour of linq2sql)
  387. //if (method.ReturnType == typeof(DateTime))
  388. //{
  389. // expression = ExpressionOptimizer.Analyze(expression);
  390. // //same behaviour that Linq2Sql
  391. // throw new NotSupportedException("Method 'System.DateTime Parse(System.String)' has no supported translation to SQL");
  392. //}
  393. }
  394. return parsed;
  395. }
  396. else
  397. throw new ArgumentException();
  398. }
  399. protected virtual Expression AnalyzeToString(MethodInfo method, IList<Expression> parameters, BuilderContext builderContext)
  400. {
  401. if (parameters.Count != 1)
  402. throw new ArgumentException();
  403. Expression parameter = parameters.First();
  404. Expression parameterToHandle;
  405. if(parameter.Type.IsNullable())
  406. parameter = Analyze(Expression.Convert(parameter, parameter.Type.GetNullableType()), builderContext);
  407. parameterToHandle = Analyze(parameter, builderContext);
  408. InputParameterExpression inputParameter = parameterToHandle as InputParameterExpression;
  409. if (inputParameter != null)
  410. {
  411. parameterToHandle = RegisterParameter(System.Linq.Expressions.Expression.Call(inputParameter.Expression, method), inputParameter.Alias, builderContext);
  412. UnregisterParameter(inputParameter, builderContext);
  413. return parameterToHandle;
  414. }
  415. if (!parameter.Type.IsPrimitive && parameterToHandle.Type != typeof(string))
  416. {
  417. //TODO: ExpressionDispacher.Analyze.AnalyzeToString is not complete
  418. //This is the standar behaviour in linq2sql, nonetheless the behaviour isn't complete since when the expression
  419. //can be executed in the clr, ie: (where new StrangeObject().ToString()) should work. The problem is that
  420. //we don't have a reference to the optimizer here.
  421. //Working samples in: /Tests/Test_Nunit/ReadTests_Conversions.cs
  422. string message = "Method ToString can only be translated to SQL for primitive types.";
  423. int? select = FirstIndexOf(builderContext.CallStack, "Select");
  424. int? where = FirstIndexOf(builderContext.CallStack, "Where");
  425. if ((where ?? int.MaxValue) < (select ?? int.MaxValue))
  426. // Assume we're generating the .Where() clause, not .Select()
  427. throw new NotSupportedException(message);
  428. // for .Select()
  429. throw new InvalidOperationException(message);
  430. }
  431. return Expression.Convert(parameterToHandle, typeof(string), typeof(Convert).GetMethod("ToString", new[] { parameterToHandle.Type }));
  432. }
  433. static int? FirstIndexOf(Stack<MethodInfo> callStack, string methodName)
  434. {
  435. int? index = null;
  436. callStack.Where((m, i) =>
  437. {
  438. if (m.Name == methodName)
  439. {
  440. index = i;
  441. return true;
  442. }
  443. return false;
  444. }).FirstOrDefault();
  445. return index;
  446. }
  447. /// <summary>
  448. /// Limits selection count
  449. /// </summary>
  450. /// <param name="parameters"></param>
  451. /// <param name="builderContext"></param>
  452. /// <returns></returns>
  453. protected virtual Expression AnalyzeTake(IList<Expression> parameters, BuilderContext builderContext)
  454. {
  455. AddLimit(Analyze(parameters[1], builderContext), builderContext);
  456. return Analyze(parameters[0], builderContext);
  457. }
  458. protected virtual void AddLimit(Expression limit, BuilderContext builderContext)
  459. {
  460. var previousLimit = builderContext.CurrentSelect.Limit;
  461. if (previousLimit != null)
  462. builderContext.CurrentSelect.Limit = Expression.Condition(Expression.LessThan(previousLimit, limit),
  463. previousLimit, limit);
  464. else
  465. builderContext.CurrentSelect.Limit = limit;
  466. }
  467. /// <summary>
  468. /// Skip selection items
  469. /// </summary>
  470. /// <param name="parameters"></param>
  471. /// <param name="builderContext"></param>
  472. /// <returns></returns>
  473. protected virtual Expression AnalyzeSkip(IList<Expression> parameters, BuilderContext builderContext)
  474. {
  475. AddOffset(Analyze(parameters[1], builderContext), builderContext);
  476. return Analyze(parameters[0], builderContext);
  477. }
  478. protected virtual void AddOffset(Expression offset, BuilderContext builderContext)
  479. {
  480. var previousOffset = builderContext.CurrentSelect.Offset;
  481. if (previousOffset != null)
  482. builderContext.CurrentSelect.Offset = Expression.Add(offset, previousOffset);
  483. else
  484. builderContext.CurrentSelect.Offset = offset;
  485. }
  486. /// <summary>
  487. /// Registers a scalar method call for result
  488. /// </summary>
  489. /// <param name="methodName"></param>
  490. /// <param name="limit"></param>
  491. /// <param name="parameters"></param>
  492. /// <param name="builderContext"></param>
  493. /// <returns></returns>
  494. protected virtual Expression AnalyzeScalar(string methodName, int? limit, IList<Expression> parameters, BuilderContext builderContext)
  495. {
  496. builderContext.CurrentSelect.ExecuteMethodName = methodName;
  497. if (limit.HasValue)
  498. AddLimit(Expression.Constant(limit.Value), builderContext);
  499. var table = Analyze(parameters[0], builderContext);
  500. var set = table as EntitySetExpression;
  501. if (set != null)
  502. table = set.TableExpression;
  503. CheckWhere(table, parameters, 1, builderContext);
  504. return table;
  505. }
  506. /// <summary>
  507. /// Some methods, like Single(), Count(), etc. can get an extra parameter, specifying a restriction.
  508. /// This method checks if the parameter is specified, and adds it to the WHERE clauses
  509. /// </summary>
  510. /// <param name="table"></param>
  511. /// <param name="parameters"></param>
  512. /// <param name="extraParameterIndex"></param>
  513. /// <param name="builderContext"></param>
  514. private void CheckWhere(Expression table, IList<Expression> parameters, int extraParameterIndex, BuilderContext builderContext)
  515. {
  516. if (parameters.Count > extraParameterIndex) // a lambda can be specified here, this is a restriction
  517. RegisterWhere(Analyze(parameters[extraParameterIndex], table, builderContext), builderContext);
  518. }
  519. /// <summary>
  520. /// Returns a projection method call
  521. /// </summary>
  522. /// <param name="specialExpressionType"></param>
  523. /// <param name="parameters"></param>
  524. /// <param name="builderContext"></param>
  525. /// <returns></returns>
  526. protected virtual Expression AnalyzeProjectionQuery(SpecialExpressionType specialExpressionType, IList<Expression> parameters,
  527. BuilderContext builderContext)
  528. {
  529. if (builderContext.IsExternalInExpressionChain)
  530. {
  531. var operand0 = Analyze(parameters[0], builderContext);
  532. Expression projectionOperand;
  533. if ( builderContext.CurrentSelect.NextSelectExpression != null
  534. || builderContext.CurrentSelect.Operands.Count() > 0
  535. || builderContext.CurrentSelect.Group.Count > 0
  536. )
  537. {
  538. //BuildSelect(builderContext.CurrentSelect, builderContext);
  539. operand0 = new SubSelectExpression(builderContext.CurrentSelect, operand0.Type, "source");
  540. builderContext.NewParentSelect();
  541. // In the new scope we should not have MaximumDatabaseLoad
  542. builderContext.QueryContext.MaximumDatabaseLoad = false;
  543. builderContext.CurrentSelect.Tables.Add(operand0 as TableExpression);
  544. }
  545. // basically, we have three options for projection methods:
  546. // - projection on grouped table (1 operand, a GroupExpression)
  547. // - projection on grouped column (2 operands, GroupExpression and ColumnExpression)
  548. // - projection on table/column, with optional restriction
  549. var groupOperand0 = operand0 as GroupExpression;
  550. if (groupOperand0 != null)
  551. {
  552. if (parameters.Count > 1)
  553. {
  554. projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression,
  555. builderContext);
  556. }
  557. else
  558. projectionOperand = Analyze(groupOperand0.GroupedExpression, builderContext);
  559. }
  560. else
  561. {
  562. projectionOperand = operand0;
  563. CheckWhere(projectionOperand, parameters, 1, builderContext);
  564. }
  565. if (projectionOperand is TableExpression)
  566. projectionOperand = RegisterTable((TableExpression)projectionOperand, builderContext);
  567. if (groupOperand0 != null)
  568. projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression);
  569. return new SpecialExpression(specialExpressionType, projectionOperand);
  570. }
  571. else
  572. {
  573. var projectionQueryBuilderContext = builderContext.NewSelect();
  574. var tableExpression = Analyze(parameters[0], projectionQueryBuilderContext);
  575. if (!(tableExpression is TableExpression) && !(tableExpression is EntitySetExpression))
  576. tableExpression = Analyze(tableExpression, projectionQueryBuilderContext);
  577. EntitySetExpression setExpression = tableExpression as EntitySetExpression;
  578. if (setExpression != null)
  579. tableExpression = setExpression.TableExpression;
  580. // from here we build a custom clause:
  581. // <anyClause> ==> "(select count(*) from <table> where <anyClause>)>0"
  582. // TODO (later...): see if some vendors support native Any operator and avoid this substitution
  583. if (parameters.Count > 1)
  584. {
  585. setExpression = tableExpression as EntitySetExpression;
  586. if (setExpression != null)
  587. tableExpression = setExpression.TableExpression;
  588. var anyClause = Analyze(parameters[1], tableExpression, projectionQueryBuilderContext);
  589. RegisterWhere(anyClause, projectionQueryBuilderContext);
  590. }
  591. projectionQueryBuilderContext.CurrentSelect = projectionQueryBuilderContext.CurrentSelect.ChangeOperands(new SpecialExpression(specialExpressionType, tableExpression));
  592. // we now switch back to current context, and compare the result with 0
  593. return projectionQueryBuilderContext.CurrentSelect;
  594. }
  595. }
  596. /// <summary>
  597. /// Entry point for a Select()
  598. /// static Select(this Expression table, λ(table))
  599. /// </summary>
  600. /// <param name="parameters"></param>
  601. /// <param name="builderContext"></param>
  602. /// <returns></returns>
  603. protected virtual Expression AnalyzeSelect(IList<Expression> parameters, BuilderContext builderContext)
  604. {
  605. // just call back the underlying lambda (or quote, whatever)
  606. Expression ex = Analyze(parameters[1], parameters[0], builderContext);
  607. // http://social.msdn.microsoft.com/Forums/en-US/linqprojectgeneral/thread/1ce25da3-44c6-407d-8395-4c146930004b
  608. if (ex.NodeType == ExpressionType.MemberInit &&
  609. builderContext.QueryContext.DataContext.Mapping.GetMetaType(ex.Type) != null)
  610. throw new NotSupportedException(
  611. string.Format("Explicit construction of entity type '{0}' in query is not allowed.",
  612. ex.Type.FullName));
  613. TableExpression tableExpression = parameters[0] as TableExpression;
  614. if (tableExpression != null && builderContext.CurrentSelect.Tables.Count == 0)
  615. RegisterTable(tableExpression, builderContext);
  616. return ex;
  617. }
  618. /// <summary>
  619. /// Entry point for a Where()
  620. /// static Where(this Expression table, λ(table))
  621. /// </summary>
  622. /// <param name="parameters"></param>
  623. /// <param name="builderContext"></param>
  624. /// <returns></returns>
  625. protected virtual Expression AnalyzeWhere(IList<Expression> parameters, BuilderContext builderContext)
  626. {
  627. var tablePiece = parameters[0];
  628. RegisterWhere(Analyze(parameters[1], tablePiece, builderContext), builderContext);
  629. return tablePiece;
  630. }
  631. /// <summary>
  632. /// Handling a lambda consists in:
  633. /// - filling its input parameters with what's on the stack
  634. /// - using the body (parameters are registered in the context)
  635. /// </summary>
  636. /// <param name="expression"></param>
  637. /// <param name="parameters"></param>
  638. /// <param name="builderContext"></param>
  639. /// <returns></returns>
  640. protected virtual Expression AnalyzeLambda(Expression expression, IList<Expression> parameters, BuilderContext builderContext)
  641. {
  642. var lambdaExpression = expression as LambdaExpression;
  643. if (lambdaExpression == null)
  644. throw Error.BadArgument("S0227: Unknown type for AnalyzeLambda() ({0})", expression.GetType());
  645. // for a lambda, first parameter is body, others are input parameters
  646. // so we create a parameters stack
  647. for (int parameterIndex = 0; parameterIndex < lambdaExpression.Parameters.Count; parameterIndex++)
  648. {
  649. var parameterExpression = lambdaExpression.Parameters[parameterIndex];
  650. builderContext.Parameters[parameterExpression.Name] = Analyze(parameters[parameterIndex], builderContext);
  651. }
  652. // we keep only the body, the header is now useless
  653. // and once the parameters have been substituted, we don't pass one anymore
  654. return Analyze(lambdaExpression.Body, builderContext);
  655. }
  656. /// <summary>
  657. /// When a parameter is used, we replace it with its original value
  658. /// </summary>
  659. /// <param name="expression"></param>
  660. /// <param name="builderContext"></param>
  661. /// <returns></returns>
  662. protected virtual Expression AnalyzeParameter(Expression expression, BuilderContext builderContext)
  663. {
  664. Expression unaliasedExpression;
  665. var parameterName = GetParameterName(expression);
  666. builderContext.Parameters.TryGetValue(parameterName, out unaliasedExpression);
  667. if (unaliasedExpression == null)
  668. throw Error.BadArgument("S0257: can not find parameter '{0}'", parameterName);
  669. #region set alias helper
  670. // for table...
  671. var unaliasedTableExpression = unaliasedExpression as TableExpression;
  672. if (unaliasedTableExpression != null && unaliasedTableExpression.Alias == null)
  673. unaliasedTableExpression.Alias = parameterName;
  674. // .. or column
  675. var unaliasedColumnExpression = unaliasedExpression as ColumnExpression;
  676. if (unaliasedColumnExpression != null && unaliasedColumnExpression.Alias == null)
  677. unaliasedColumnExpression.Alias = parameterName;
  678. #endregion
  679. //var groupByExpression = unaliasedExpression as GroupByExpression;
  680. //if (groupByExpression != null)
  681. // unaliasedExpression = groupByExpression.ColumnExpression.Table;
  682. return unaliasedExpression;
  683. }
  684. /// <summary>
  685. /// Returns if the given member can be considered as an EntitySet<>
  686. /// </summary>
  687. /// <param name="memberType"></param>
  688. /// <param name="entityType"></param>
  689. /// <returns></returns>
  690. protected virtual bool IsEntitySet(Type memberType, out Type entityType)
  691. {
  692. entityType = memberType;
  693. // one check, a generic EntityRef<> or inherited
  694. if (memberType.IsGenericType && typeof(EntitySet<>).IsAssignableFrom(memberType.GetGenericTypeDefinition()))
  695. {
  696. entityType = memberType.GetGenericArguments()[0];
  697. return true;
  698. }
  699. #if !MONO_STRICT
  700. // this is for compatibility with previously generated .cs files
  701. // TODO: remove in 2009
  702. if (memberType.IsGenericType && typeof(System.Data.Linq.EntitySet<>).IsAssignableFrom(memberType.GetGenericTypeDefinition()))
  703. {
  704. entityType = memberType.GetGenericArguments()[0];
  705. return true;
  706. }
  707. #endif
  708. return false;
  709. }
  710. /// <summary>
  711. /// Analyzes a member access.
  712. /// This analyzis is down to top: the highest identifier is at bottom
  713. /// </summary>
  714. /// <param name="expression"></param>
  715. /// <param name="builderContext"></param>
  716. /// <returns></returns>
  717. protected virtual Expression AnalyzeMember(Expression expression, BuilderContext builderContext)
  718. {
  719. var memberExpression = (MemberExpression)expression;
  720. Expression objectExpression = null;
  721. //maybe is a static member access like DateTime.Now
  722. bool isStaticMemberAccess = memberExpression.Member.GetIsStaticMember();
  723. var memberInfo = memberExpression.Member;
  724. if (!isStaticMemberAccess && memberInfo.Name == "Count")
  725. return AnalyzeProjectionQuery(SpecialExpressionType.Count, new[] { memberExpression.Expression }, builderContext);
  726. if (!isStaticMemberAccess)
  727. // first parameter is object, second is member
  728. objectExpression = Analyze(memberExpression.Expression, builderContext);
  729. // then see what we can do, depending on object type
  730. // - MetaTable --> then the result is a table
  731. // - Table --> the result may be a column or a join
  732. // - Object --> external parameter or table (can this happen here? probably not... to be checked)
  733. EntitySetExpression setExpression = objectExpression as EntitySetExpression;
  734. if (setExpression != null)
  735. {
  736. objectExpression = setExpression.TableExpression;
  737. }
  738. if (objectExpression is MetaTableExpression)
  739. {
  740. var metaTableExpression = (MetaTableExpression)objectExpression;
  741. var tableExpression = metaTableExpression.GetTableExpression(memberInfo);
  742. if (tableExpression == null)
  743. throw Error.BadArgument("S0270: MemberInfo '{0}' not found in MetaTable", memberInfo.Name);
  744. return tableExpression;
  745. }
  746. if (objectExpression is GroupExpression)
  747. {
  748. if (memberInfo.Name == "Key")
  749. return ((GroupExpression)objectExpression).KeyExpression;
  750. }
  751. // if object is a table, then we need a column, or an association
  752. if (objectExpression is TableExpression)
  753. {
  754. var tableExpression = (TableExpression)objectExpression;
  755. // before finding an association, we check for an EntitySet<>
  756. // this will be used in RegisterAssociation
  757. Type entityType;
  758. if (IsEntitySet(memberInfo.GetMemberType(), out entityType))
  759. return new EntitySetExpression(tableExpression, memberInfo, memberInfo.GetMemberType(), builderContext, this);
  760. // first of all, then, try to find the association
  761. var queryAssociationExpression = RegisterAssociation(tableExpression, memberInfo, entityType,
  762. builderContext);
  763. if (queryAssociationExpression != null)
  764. {
  765. return queryAssociationExpression;
  766. }
  767. // then, try the column
  768. ColumnExpression queryColumnExpression = RegisterColumn(tableExpression, memberInfo, builderContext);
  769. if (queryColumnExpression != null)
  770. {
  771. Type storageType = queryColumnExpression.StorageInfo != null ? queryColumnExpression.StorageInfo.GetMemberType() : null;
  772. if (storageType != null && queryColumnExpression.Type != storageType)
  773. {
  774. return Expression.Convert(queryColumnExpression, queryColumnExpression.Type, typeof(Convert).GetMethod("To" + queryColumnExpression.Type.Name, new Type[] { queryColumnExpression.Type }));
  775. }
  776. else
  777. {
  778. return queryColumnExpression;
  779. }
  780. }
  781. // then, cry
  782. throw Error.BadArgument("S0293: Column must be mapped. Non-mapped columns are not handled by now.");
  783. }
  784. // if object is still an object (== a constant), then we have an external parameter
  785. if (objectExpression is ConstantExpression)
  786. {
  787. // the memberInfo.Name is provided here only to ease the SQL reading
  788. var parameterExpression = RegisterParameter(expression, memberInfo.Name, builderContext);
  789. if (parameterExpression != null)
  790. return parameterExpression;
  791. throw Error.BadArgument("S0302: Can not created parameter from expression '{0}'", expression);
  792. }
  793. // we have here a special cases for nullables
  794. if (!isStaticMemberAccess && objectExpression.Type != null && objectExpression.Type.IsNullable())
  795. {
  796. // Value means we convert the nullable to a value --> use Convert instead (works both on CLR and SQL, too)
  797. if (memberInfo.Name == "Value")
  798. return Expression.Convert(objectExpression, memberInfo.GetMemberType());
  799. // HasValue means not null (works both on CLR and SQL, too)
  800. if (memberInfo.Name == "HasValue")
  801. return new SpecialExpression(SpecialExpressionType.IsNotNull, objectExpression);
  802. }
  803. if (memberInfo.DeclaringType == typeof(DateTime))
  804. return AnalyzeDateTimeMemberAccess(objectExpression, memberInfo, isStaticMemberAccess);
  805. // TODO: make this expresion safe (objectExpression can be null here)
  806. if (objectExpression.Type == typeof(TimeSpan))
  807. return AnalyzeTimeSpanMemberAccess(objectExpression, memberInfo);
  808. if (objectExpression is InputParameterExpression)
  809. {
  810. return AnalyzeExternalParameterMember((InputParameterExpression)objectExpression, memberInfo, builderContext);
  811. }
  812. if (objectExpression is MemberInitExpression)
  813. {
  814. var foundExpression = AnalyzeMemberInit((MemberInitExpression)objectExpression, memberInfo, builderContext);
  815. if (foundExpression != null)
  816. return foundExpression;
  817. }
  818. return AnalyzeCommonMember(objectExpression, memberInfo, builderContext);
  819. }
  820. protected Expression AnalyzeTimeSpanMemberAccess(Expression objectExpression, MemberInfo memberInfo)
  821. {
  822. //A timespan expression can be only generated in a c# query as a DateTime difference, as a function call return or as a paramter
  823. //this case is for the DateTime difference operation
  824. if (!(objectExpression is BinaryExpression))
  825. throw new NotSupportedException();
  826. var operands = objectExpression.GetOperands();
  827. bool absoluteSpam = memberInfo.Name.StartsWith("Total");
  828. string operationKey = absoluteSpam ? memberInfo.Name.Substring(5) : memberInfo.Name;
  829. Expression currentExpression;
  830. switch (operationKey)
  831. {
  832. case "Milliseconds":
  833. currentExpression = Expression.Convert(new SpecialExpression(SpecialExpressionType.DateDiffInMilliseconds, operands.First(), operands.ElementAt(1)), typeof(double));
  834. break;
  835. case "Seconds":
  836. currentExpression = Expression.Divide(
  837. Expression.Convert(new SpecialExpression(SpecialExpressionType.DateDiffInMilliseconds, operands.First(), operands.ElementAt(1)), typeof(double)),
  838. Expression.Constant(1000.0));
  839. break;
  840. case "Minutes":
  841. currentExpression = Expression.Divide(
  842. Expression.Convert(new SpecialExpression(SpecialExpressionType.DateDiffInMilliseconds, operands.First(), operands.ElementAt(1)), typeof(double)),
  843. Expression.Constant(60000.0));
  844. break;
  845. case "Hours":
  846. currentExpression = Expression.Divide(
  847. Expression.Convert(new SpecialExpression(SpecialExpressionType.DateDiffInMilliseconds, operands.First(), operands.ElementAt(1)), typeof(double)),
  848. Expression.Constant(3600000.0));
  849. break;
  850. case "Days":
  851. currentExpression = Expression.Divide(
  852. Expression.Convert(new SpecialExpression(SpecialExpressionType.DateDiffInMilliseconds, operands.First(), operands.ElementAt(1)), typeof(double)),
  853. Expression.Constant(86400000.0));
  854. break;
  855. default:
  856. throw new NotSupportedException(string.Format("The operation {0} over the TimeSpan isn't currently supported", memberInfo.Name));
  857. }
  858. if (!absoluteSpam)
  859. {
  860. switch (memberInfo.Name)
  861. {
  862. case "Milliseconds":
  863. currentExpression = Expression.Convert(Expression.Modulo(Expression.Convert(currentExpression, typeof(long)), Expression.Constant(1000L)), typeof(int));
  864. break;
  865. case "Seconds":
  866. currentExpression = Expression.Convert(Expression.Modulo(Expression.Convert(currentExpression, typeof(long)),
  867. Expression.Constant(60L)), typeof(int));
  868. break;
  869. case "Minutes":
  870. currentExpression = Expression.Convert(Expression.Modulo(Expression.Convert(currentExpression, typeof(long)),
  871. Expression.Constant(60L)), typeof(int));
  872. break;
  873. case "Hours":
  874. currentExpression = Expression.Convert(Expression.Modulo(Expression.Convert(
  875. currentExpression, typeof(long)),
  876. Expression.Constant(24L)), typeof(int));
  877. break;
  878. case "Days":
  879. currentExpression = Expression.Convert(currentExpression, typeof(int));
  880. break;
  881. }
  882. }
  883. return currentExpression;
  884. }
  885. protected Expression AnalyzeDateTimeMemberAccess(Expression objectExpression, MemberInfo memberInfo, bool isStaticMemberAccess)
  886. {
  887. if (isStaticMemberAccess)
  888. {
  889. if (memberInfo.Name == "Now")
  890. return new SpecialExpression(SpecialExpressionType.Now);
  891. else
  892. throw new NotSupportedException(string.Format("DateTime Member access {0} not supported", memberInfo.Name));
  893. }
  894. else
  895. {
  896. switch (memberInfo.Name)
  897. {
  898. case "Year":
  899. return new SpecialExpression(SpecialExpressionType.Year, objectExpression);
  900. case "Month":
  901. return new SpecialExpression(SpecialExpressionType.Month, objectExpression);
  902. case "Day":
  903. return new SpecialExpression(SpecialExpressionType.Day, objectExpression);
  904. case "Hour":
  905. return new SpecialExpression(SpecialExpressionType.Hour, objectExpression);
  906. case "Minute":
  907. return new SpecialExpression(SpecialExpressionType.Minute, objectExpression);
  908. case "Second":
  909. return new SpecialExpression(SpecialExpressionType.Second, objectExpression);
  910. case "Millisecond":
  911. return new SpecialExpression(SpecialExpressionType.Millisecond, objectExpression);
  912. case "Date":
  913. return new SpecialExpression(SpecialExpressionType.Date, objectExpression);
  914. default:
  915. throw new NotSupportedException(string.Format("DateTime Member access {0} not supported", memberInfo.Name));
  916. }
  917. }
  918. }
  919. /// <summary>
  920. /// This method analyzes the case of a new followed by a member access
  921. /// for example new "A(M = value).M", where the Expression can be reduced to "value"
  922. /// Caution: it may return null if no result is found
  923. /// </summary>
  924. /// <param name="expression"></param>
  925. /// <param name="memberInfo"></param>
  926. /// <param name="builderContext"></param>
  927. /// <returns>A member initializer or null</returns>
  928. protected virtual Expression AnalyzeMemberInit(MemberInitExpression expression, MemberInfo memberInfo,
  929. BuilderContext builderContext)
  930. {
  931. // TODO: a method for NewExpression that we will use directly from AnalyzeMember and indirectly from here
  932. foreach (var binding in expression.Bindings)
  933. {
  934. var memberAssignment = binding as MemberAssignment;
  935. if (memberAssignment != null)
  936. {
  937. if (memberAssignment.Member == memberInfo)
  938. return memberAssignment.Expression;
  939. }
  940. }
  941. return null;
  942. }
  943. protected virtual Expression AnalyzeExternalParameterMember(InputParameterExpression expression, MemberInfo memberInfo, BuilderContext builderContext)
  944. {
  945. UnregisterParameter(expression, builderContext);
  946. return RegisterParameter(Expression.MakeMemberAccess(expression.Expression, memberInfo), memberInfo.Name, builderContext);
  947. }
  948. protected virtual Expression AnalyzeCommonMember(Expression objectExpression, MemberInfo memberInfo, BuilderContext builderContext)
  949. {
  950. if (typeof(string).IsAssignableFrom(objectExpression.Type))
  951. {
  952. switch (memberInfo.Name)
  953. {
  954. case "Length":
  955. return new SpecialExpression(SpecialExpressionType.StringLength, objectExpression);
  956. }
  957. }
  958. //throw Error.BadArgument("S0324: Don't know how to handle Piece");
  959. return Expression.MakeMemberAccess(objectExpression, memberInfo);
  960. }
  961. /// <summary>
  962. /// A Quote creates a new local context, outside which created parameters disappear
  963. /// This is why we clone the BuilderContext
  964. /// </summary>
  965. /// <param name="piece"></param>
  966. /// <param name="parameters"></param>
  967. /// <param name="builderContext"></param>
  968. /// <returns></returns>
  969. protected virtual Expression AnalyzeQuote(Expression piece, IList<Expression> parameters, BuilderContext builderContext)
  970. {
  971. var builderContextClone = builderContext.NewQuote();
  972. var firstExpression = piece.GetOperands().First();
  973. return Analyze(firstExpression, parameters, builderContextClone);
  974. }
  975. /// <summary>
  976. /// </summary>
  977. /// <param name="expression"></param>
  978. /// <param name="builderContext"></param>
  979. /// <returns></returns>
  980. protected virtual Expression AnalyzeOperatorSubstract(Expression expression, BuilderContext builderContext)
  981. {
  982. return AnalyzeOperator(expression, builderContext);
  983. }
  984. /// <summary>
  985. /// Operator analysis consists in anlyzing all operands
  986. /// </summary>
  987. /// <param name="expression"></param>
  988. /// <param name="builderContext"></param>
  989. /// <returns></returns>
  990. protected virtual Expression AnalyzeOperator(Expression expression, BuilderContext builderContext)
  991. {
  992. var u = expression as UnaryExpression;
  993. string parameterName;
  994. if (expression.NodeType == ExpressionType.Convert &&
  995. u.Method == null &&
  996. (parameterName = GetParameterName(u.Operand)) != null)
  997. {
  998. Expression unaliasedExpression;
  999. builderContext.Parameters.TryGetValue(parameterName, out unaliasedExpression);
  1000. var unaliasedTableExpression = unaliasedExpression as TableExpression;
  1001. if (unaliasedExpression != null && unaliasedTableExpression != null)
  1002. return unaliasedTableExpression;
  1003. }
  1004. var operands = expression.GetOperands().ToList();
  1005. for (int operandIndex = 0; operandIndex < operands.Count; operandIndex++)
  1006. {
  1007. var operand = operands[operandIndex];
  1008. operands[operandIndex] = Analyze(operand, builderContext);
  1009. }
  1010. return expression.ChangeOperands(operands);
  1011. }
  1012. protected virtual Expression AnalyzeNewOperator(Expression expression, BuilderContext builderContext)
  1013. {
  1014. if (builderContext.ExpectMetaTableDefinition)
  1015. {
  1016. // first, check if we have a MetaTable definition
  1017. Type metaType;
  1018. var typeInitializers = GetTypeInitializers<Expression>((NewExpression)expression, true, out metaType);
  1019. var aliases = new Dictionary<MemberInfo, MutableExpression>();
  1020. foreach (var memberInfo in typeInitializers.Keys)
  1021. {
  1022. var e = Analyze(typeInitializers[memberInfo], builderContext);
  1023. var tableExpression = e as TableExpression;
  1024. var ese = e as EntitySetExpression;
  1025. if (ese != null)
  1026. tableExpression = ese.TableExpression;
  1027. if (tableExpression != null)
  1028. {
  1029. aliases[memberInfo] = tableExpression;
  1030. }
  1031. else
  1032. {
  1033. aliases[memberInfo] = Analyze(typeInitializers[memberInfo], builderContext) as MetaTableExpression;
  1034. }
  1035. }
  1036. if (IsMetaTableDefinition(aliases))
  1037. return RegisterMetaTable(metaType, aliases, builderContext);
  1038. }
  1039. return AnalyzeOperator(expression, builderContext);
  1040. }
  1041. protected virtual bool IsMetaTableDefinition(IDictionary<MemberInfo, MutableExpression> aliases)
  1042. {
  1043. if (aliases.Count != 2)
  1044. return false;
  1045. foreach (var tableExpression in aliases.Values)
  1046. {
  1047. if (tableExpression == null)
  1048. return false;
  1049. }
  1050. return true;
  1051. }
  1052. /// <summary>
  1053. /// SelectMany() joins tables
  1054. /// </summary>
  1055. /// <param name="parameters"></param>
  1056. /// <param name="builderContext"></param>
  1057. /// <returns></returns>
  1058. protected virtual Expression AnalyzeSelectMany(IList<Expression> parameters, BuilderContext builderContext)
  1059. {
  1060. if (parameters.Count == 3)
  1061. {
  1062. // ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system.core/html/3371348f-7811-b0bc-8c0a-2a595e08e086.htm
  1063. var tableExpression = parameters[0];
  1064. var projectionExpression = Analyze(parameters[1], new[] { tableExpression }, builderContext);
  1065. //var manyPiece = Analyze(parameters[2], new[] { tableExpression, projectionExpression }, builderContext);
  1066. // from here, our manyPiece is a MetaTable definition
  1067. //var newExpression = manyPiece as NewExpression;
  1068. //if (newExpression == null)
  1069. // throw Error.BadArgument("S0377: Expected a NewExpression as SelectMany() return value");
  1070. //Type metaTableType;
  1071. //var associations = GetTypeInitializers<TableExpression>(newExpression, true, out metaTableType);
  1072. //return RegisterMetaTable(metaTableType, associations, builderContext);
  1073. var metaTableDefinitionBuilderContext = builderContext.Clone();
  1074. metaTableDefinitionBuilderContext.ExpectMetaTableDefinition = true;
  1075. var expression = Analyze(parameters[2], new[] { tableExpression, projectionExpression },
  1076. metaTableDefinitionBuilderContext);
  1077. return expression;
  1078. }
  1079. throw Error.BadArgument("S0358: Don't know how to handle this SelectMany() overload ({0} parameters)", parameters.Count);
  1080. }
  1081. protected virtual IDictionary<MemberInfo, E> GetTypeInitializers<E>(NewExpression newExpression,
  1082. bool checkCast, out Type metaType)
  1083. where E : Expression
  1084. {
  1085. var associations = new Dictionary<MemberInfo, E>();
  1086. metaType = null;
  1087. for (int ctorParameterIndex = 0; ctorParameterIndex < newExpression.Arguments.Count; ctorParameterIndex++)
  1088. {
  1089. var aliasedExpression = newExpression.Arguments[ctorParameterIndex] as E;
  1090. if (aliasedExpression == null && checkCast)
  1091. throw Error.BadArgument("S0541: Expected an specific Expression type for GetTypeInitializers()");
  1092. var memberInfo = newExpression.Members[ctorParameterIndex];
  1093. metaType = memberInfo.ReflectedType;
  1094. // the property info is the reflecting property for the memberInfo, if memberInfo is a get_*
  1095. // otherwise we keep the memberInfo as is, since it is a field
  1096. var propertyInfo = memberInfo.GetExposingProperty() ?? memberInfo;
  1097. associations[propertyInfo] = aliasedExpression;
  1098. }
  1099. if (metaType == null && checkCast)
  1100. throw Error.BadArgument("S0550: Empty NewExpression found"); // this should never happen, otherwise we may simply ignore it or take the type from elsewhere
  1101. return associations;
  1102. }
  1103. //protected virtual IDictionary<MemberInfo, E> GetTypeInitializers<E>(NewExpression newExpression)
  1104. // where E : Expression
  1105. //{
  1106. // Type metaType;
  1107. // return GetTypeInitializers<E>(newExpression, out metaType);
  1108. //}
  1109. /// <summary>
  1110. /// Analyzes a Join statement (explicit join)
  1111. /// </summary>
  1112. /// <param name="parameters"></param>
  1113. /// <param name="builderContext"></param>
  1114. /// <returns></returns>
  1115. protected virtual Expression AnalyzeJoin(IList<Expression> parameters, BuilderContext builderContext)
  1116. {
  1117. return AnalyzeJoin(parameters, TableJoinType.Inner, builderContext);
  1118. }
  1119. /// <summary>
  1120. /// Analyzes a Join statement (explicit join)
  1121. /// </summary>
  1122. /// <param name="parameters"></param>
  1123. /// <param name="builderContext"></param>
  1124. /// <returns></returns>
  1125. protected virtual Expression AnalyzeGroupJoin(IList<Expression> parameters, BuilderContext builderContext)
  1126. {
  1127. return AnalyzeJoin(parameters, TableJoinType.Inner, builderContext);
  1128. }
  1129. protected virtual Expression AnalyzeOuterJoin(IList<Expression> parameters, BuilderContext builderContext)
  1130. {
  1131. var expression = Analyze(parameters[0], builderContext);
  1132. var tableExpression = expression as TableExpression;
  1133. if (tableExpression != null)
  1134. {
  1135. tableExpression.SetOuterJoin();
  1136. }
  1137. return expression;
  1138. }
  1139. private Expression AnalyzeJoin(IList<Expression> parameters, TableJoinType joinType, BuilderContext builderContext)
  1140. {
  1141. if (parameters.Count == 5)
  1142. {
  1143. var leftExpression = Analyze(parameters[0], builderContext);
  1144. var rightTable = Analyze(parameters[1], builderContext) as TableExpression;
  1145. if (rightTable == null)
  1146. throw Error.BadArgument("S0536: Expected a TableExpression for Join");
  1147. var leftJoin = Analyze(parameters[2], leftExpression, builderContext);
  1148. var rightJoin = Analyze(parameters[3], rightTable, builderContext);
  1149. // from here, we have two options to join:
  1150. // 1. left and right are tables, we can use generic expressions (most common)
  1151. // 2. left is something else (a meta table)
  1152. var leftTable = leftExpression as TableExpression;
  1153. if (leftTable == null)
  1154. {
  1155. var leftColumn = leftJoin as ColumnExpression;
  1156. if (leftColumn == null)
  1157. throw Error.BadArgument("S0701: No way to find left table for Join");
  1158. leftTable = leftColumn.Table;
  1159. }
  1160. rightTable.Join(joinType, leftTable, Expression.Equal(leftJoin, rightJoin),
  1161. string.Format("join{0}", builderContext.EnumerateAllTables().Count()));
  1162. // last part is lambda, with two tables as parameters
  1163. var metaTableDefinitionBuilderContext = builderContext.Clone();
  1164. metaTableDefinitionBuilderContext.ExpectMetaTableDefinition = true;
  1165. var expression = Analyze(parameters[4], new[] { leftExpression, rightTable }, metaTableDefinitionBuilderContext);
  1166. return expression;
  1167. }
  1168. throw Error.BadArgument("S0530: Don't know how to handle GroupJoin() with {0} parameters", parameters.Count);
  1169. }
  1170. /// <summary>
  1171. /// "Distinct" means select X group by X
  1172. /// </summary>
  1173. /// <param name="parameters"></param>
  1174. /// <param name="builderContext"></param>
  1175. /// <returns></returns>
  1176. protected virtual Expression AnalyzeDistinct(IList<Expression> parameters, BuilderContext builderContext)
  1177. {
  1178. var expression = Analyze(parameters[0], builderContext);
  1179. // we select and group by the same criterion
  1180. var group = new GroupExpression(expression, expression);
  1181. if (builderContext.CurrentSelect.NextSelectExpression != null)
  1182. {
  1183. expression = new SubSelectExpression(builderContext.CurrentSelect, expression.Type, "source");
  1184. builderContext.NewParentSelect();
  1185. // In the new scope we should not have MaximumDatabaseLoad
  1186. builderContext.QueryContext.MaximumDatabaseLoad = false;
  1187. builderContext.CurrentSelect.Tables.Add(expression as TableExpression);
  1188. }
  1189. builderContext.CurrentSelect.Group.Add(group);
  1190. // "Distinct" method is equivalent to a GroupBy
  1191. // but for some obscure reasons, Linq expects a IQueryable instead of an IGrouping
  1192. // so we return the column, not the group
  1193. return expression;
  1194. }
  1195. /// <summary>
  1196. /// Creates a group by clause
  1197. /// </summary>
  1198. /// <param name="parameters"></param>
  1199. /// <param name="builderContext"></param>
  1200. /// <returns></returns>
  1201. protected virtual Expression AnalyzeGroupBy(IList<Expression> parameters, BuilderContext builderContext)
  1202. {
  1203. var table = Analyze(parameters[0], builderContext);
  1204. var keyExpression = Analyze(parameters[1], table, builderContext);
  1205. Expression result;
  1206. if (parameters.Count == 2)
  1207. result = table; // we return the whole table
  1208. else if (parameters.Count == 3)
  1209. result = Analyze(parameters[2], table, builderContext); // 3 parameters for a projection expression
  1210. else
  1211. throw Error.BadArgument("S0629: Don't know how to handle Expression to group by with {0} parameters", parameters.Count);
  1212. var group = new GroupExpression(result, keyExpression);
  1213. builderContext.CurrentSelect.Group.Add(group);
  1214. return group;
  1215. }
  1216. /// <summary>
  1217. /// All() returns true if the given condition satisfies all provided elements
  1218. /// </summary>
  1219. /// <param name="parameters"></param>
  1220. /// <param name="builderContext"></param>
  1221. /// <returns></returns>
  1222. protected virtual Expression AnalyzeAll(IList<Expression> parameters, BuilderContext builderContext)
  1223. {
  1224. var allBuilderContext = builderContext.NewSelect();
  1225. var tableExpression = Analyze(parameters[0], allBuilderContext);
  1226. var allClause = Analyze(parameters[1], tableExpression, allBuilderContext);
  1227. // from here we build a custom clause:
  1228. // <allClause> ==> "(select count(*) from <table> where not <allClause>)==0"
  1229. // TODO (later...): see if some vendors support native All operator and avoid this substitution
  1230. var whereExpression = Expression.Not(allClause);
  1231. RegisterWhere(whereExpression, allBuilderContext);
  1232. allBuilderContext.CurrentSelect = allBuilderContext.CurrentSelect.ChangeOperands(new SpecialExpression(SpecialExpressionType.Count, tableExpression));
  1233. // TODO: see if we need to register the tablePiece here (we probably don't)
  1234. // we now switch back to current context, and compare the result with 0
  1235. var allExpression = Expression.Equal(allBuilderContext.CurrentSelect, Expression.Constant(0));
  1236. return allExpression;
  1237. }
  1238. /// <summary>
  1239. /// Any() returns true if the given condition satisfies at least one of provided elements
  1240. /// </summary>
  1241. /// <param name="parameters"></param>
  1242. /// <param name="builderContext"></param>
  1243. /// <returns></returns>
  1244. protected virtual Expression AnalyzeAny(IList<Expression> parameters, BuilderContext builderContext)
  1245. {
  1246. if (builderContext.IsExternalInExpressionChain)
  1247. {
  1248. var tableExpression = Analyze(parameters[0], builderContext);
  1249. Expression projectionOperand;
  1250. if (builderContext.CurrentSelect.NextSelectExpression != null)
  1251. {
  1252. TableExpression currentTableExpression = tableExpression as TableExpression;
  1253. tableExpression = new SubSelectExpression(builderContext.CurrentSelect, currentTableExpression.Type, "source");
  1254. builderContext.NewParentSelect();
  1255. // In the new scope we should not have MaximumDatabaseLoad
  1256. builderContext.QueryContext.MaximumDatabaseLoad = false;
  1257. builderContext.CurrentSelect.Tables.Add(tableExpression as TableExpression);
  1258. }
  1259. // basically, we have three options for projection methods:
  1260. // - projection on grouped table (1 operand, a GroupExpression)
  1261. // - projection on grouped column (2 operands, GroupExpression and ColumnExpression)
  1262. // - projection on table/column, with optional restriction
  1263. var groupOperand0 = tableExpression as GroupExpression;
  1264. if (groupOperand0 != null)
  1265. {
  1266. if (parameters.Count > 1)
  1267. {
  1268. projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression,
  1269. builderContext);
  1270. }
  1271. else
  1272. projectionOperand = Analyze(groupOperand0.GroupedExpression, builderContext);
  1273. }
  1274. else
  1275. {
  1276. projectionOperand = tableExpression;
  1277. CheckWhere(projectionOperand, parameters, 1, builderContext);
  1278. }
  1279. if (projectionOperand is TableExpression)
  1280. projectionOperand = RegisterTable((TableExpression)projectionOperand, builderContext);
  1281. if (groupOperand0 != null)
  1282. projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression);
  1283. return Expression.GreaterThan(new SpecialExpression(SpecialExpressionType.Count, projectionOperand), Expression.Constant(0));
  1284. }
  1285. else
  1286. {
  1287. var anyBuilderContext = builderContext.NewSelect();
  1288. var tableExpression = Analyze(parameters[0], anyBuilderContext);
  1289. if (!(tableExpression is TableExpression) && !(tableExpression is EntitySetExpression))
  1290. tableExpression = Analyze(tableExpression, anyBuilderContext);
  1291. EntitySetExpression setExpression = tableExpression as EntitySetExpression;
  1292. if (setExpression != null)
  1293. tableExpression = setExpression.TableExpression;
  1294. // from here we build a custom clause:
  1295. // <anyClause> ==> "(select count(*) from <table> where <anyClause>)>0"
  1296. // TODO (later...): see if some vendors support native Any operator and avoid this substitution
  1297. if (parameters.Count > 1)
  1298. {
  1299. setExpression = tableExpression as EntitySetExpression;
  1300. if (setExpression != null)
  1301. tableExpression = setExpression.TableExpression;
  1302. var anyClause = Analyze(parameters[1], tableExpression, anyBuilderContext);
  1303. RegisterWhere(anyClause, anyBuilderContext);
  1304. }
  1305. anyBuilderContext.CurrentSelect = anyBuilderContext.CurrentSelect.ChangeOperands(new SpecialExpression(SpecialExpressionType.Count, tableExpression));
  1306. // TODO: see if we need to register the tablePiece here (we probably don't)
  1307. // we now switch back to current context, and compare the result with 0
  1308. var anyExpression = Expression.GreaterThan(anyBuilderContext.CurrentSelect, Expression.Constant(0));
  1309. return anyExpression;
  1310. }
  1311. }
  1312. protected virtual Expression AnalyzeLikeStart(IList<Expression> parameters, BuilderContext builderContext)
  1313. {
  1314. return AnalyzeLike(parameters[0], null, parameters[1], "%", builderContext);
  1315. }
  1316. protected virtual Expression AnalyzeLikeEnd(IList<Expression> parameters, BuilderContext builderContext)
  1317. {
  1318. return AnalyzeLike(parameters[0], "%", parameters[1], null, builderContext);
  1319. }
  1320. protected virtual Expression AnalyzeLike(IList<Expression> parameters, BuilderContext builderContext)
  1321. {
  1322. return AnalyzeLike(parameters[0], "%", parameters[1], "%", builderContext);
  1323. }
  1324. protected virtual Expression AnalyzeLike(Expression value, string before, Expression operand, string after, BuilderContext builderContext)
  1325. {
  1326. operand = Analyze(operand, builderContext);
  1327. if (before != null)
  1328. operand = new SpecialExpression(SpecialExpressionType.Concat, Expression.Constant(before), operand);
  1329. if (after != null)
  1330. operand = new SpecialExpression(SpecialExpressionType.Concat, operand, Expression.Constant(after));
  1331. return new SpecialExpression(SpecialExpressionType.Like, Analyze(value, builderContext), operand);
  1332. }
  1333. protected virtual Expression AnalyzeSubString(IList<Expression> parameters, BuilderContext builderContext)
  1334. {
  1335. var stringExpression = Analyze(parameters[0], builderContext);
  1336. var startExpression = new StartIndexOffsetExpression(builderContext.QueryContext.DataContext.Vendor.SqlProvider.StringIndexStartsAtOne,
  1337. Analyze(parameters[1], builderContext));
  1338. if (parameters.Count > 2)
  1339. {
  1340. var lengthExpression = parameters[2];
  1341. return new SpecialExpression(SpecialExpressionType.Substring, stringExpression, startExpression, lengthExpression);
  1342. }
  1343. return new SpecialExpression(SpecialExpressionType.Substring, stringExpression, startExpression);
  1344. }
  1345. protected virtual Expression AnalyzeContains(IList<Expression> parameters, BuilderContext builderContext)
  1346. {
  1347. if (parameters[0].Type.IsArray)
  1348. {
  1349. Expression array = Analyze(parameters[0], builderContext);
  1350. var expression = Analyze(parameters[1], builderContext);
  1351. return new SpecialExpression(SpecialExpressionType.In, expression, array);
  1352. }
  1353. else
  1354. {
  1355. if (typeof(IQueryable).IsAssignableFrom(parameters[0].Type))
  1356. {
  1357. Expression p0 = Analyze(parameters[1], builderContext);
  1358. BuilderContext newContext = builderContext.NewSelect();
  1359. InputParameterExpression ip1 = new InputParameterExpression(parameters[0], "dummy");
  1360. Expression p1 = AnalyzeQueryProvider(ip1.GetValue() as QueryProvider, newContext);
  1361. ColumnExpression c = p1 as ColumnExpression;
  1362. if (!newContext.CurrentSelect.Tables.Contains(c.Table))
  1363. {
  1364. newContext.CurrentSelect.Tables.Add(c.Table);
  1365. }
  1366. // TODO: verify if this is the right place to work
  1367. return new SpecialExpression(SpecialExpressionType.In, p0, newContext.CurrentSelect.Mutate(new Expression[] { p1 }));
  1368. }
  1369. }
  1370. throw Error.BadArgument("S0548: Can't analyze Contains() method");
  1371. }
  1372. protected virtual Expression AnalyzeToUpper(IList<Expression> parameters, BuilderContext builderContext)
  1373. {
  1374. return new SpecialExpression(SpecialExpressionType.ToUpper, Analyze(parameters[0], builderContext));
  1375. }
  1376. protected virtual Expression AnalyzeToLower(IList<Expression> parameters, BuilderContext builderContext)
  1377. {
  1378. return new SpecialExpression(SpecialExpressionType.ToLower, Analyze(parameters[0], builderContext));
  1379. }
  1380. /// <summary>
  1381. /// Registers ordering request
  1382. /// </summary>
  1383. /// <param name="parameters"></param>
  1384. /// <param name="descending"></param>
  1385. /// <param name="builderContext"></param>
  1386. /// <returns></returns>
  1387. protected virtual Expression AnalyzeOrderBy(IList<Expression> parameters, bool descending, BuilderContext builderContext)
  1388. {
  1389. var table = Analyze(parameters[0], builderContext);
  1390. // the column is related to table
  1391. var column = Analyze(parameters[1], table, builderContext);
  1392. builderContext.CurrentSelect.OrderBy.Add(new OrderByExpression(descending, column));
  1393. return table;
  1394. }
  1395. /// <summary>
  1396. /// Analyzes constant expression value, and eventually extracts a table
  1397. /// </summary>
  1398. /// <param name="expression"></param>
  1399. /// <param name="builderContext"></param>
  1400. /// <returns></returns>
  1401. protected virtual Expression AnalyzeConstant(Expression expression, BuilderContext builderContext)
  1402. {
  1403. var constantExpression = expression as ConstantExpression;
  1404. if (constantExpression != null)
  1405. {
  1406. var queriedType = GetQueriedType(expression);
  1407. if (queriedType != null)
  1408. {
  1409. //return new TableExpression(queriedType, DataMapper.GetTableName(queriedType, builderContext.QueryContext.DataContext));
  1410. }
  1411. if (constantExpression.Value is ITable)
  1412. {
  1413. var tableType = constantExpression.Type.GetGenericArguments()[0];
  1414. return CreateTable(tableType, builderContext);
  1415. }
  1416. else
  1417. {
  1418. QueryProvider queryProvider = constantExpression.Value as QueryProvider;
  1419. if (queryProvider != null)
  1420. {
  1421. Expression tableExpression = AnalyzeQueryProvider(queryProvider, builderContext.NewQuote());
  1422. return tableExpression;
  1423. }
  1424. }
  1425. }
  1426. return expression;
  1427. }
  1428. protected virtual Expression AnalyzeQueryProvider(QueryProvider queryProvider, BuilderContext builderContext)
  1429. {
  1430. // TODO: check if the QueryProvider queryProvider belong to DataContext present in builderContext.QueryContext.DataContext
  1431. // otherwise strange things could happen in the future (I suppose)
  1432. // Build a new Context for the query
  1433. ExpressionChain expressions = queryProvider.ExpressionChain;
  1434. Expression tableExpression = CreateTableExpression(queryProvider.ExpressionChain.Expressions[0], builderContext);
  1435. return this.Analyze(expressions, tableExpression, builderContext);
  1436. }
  1437. protected virtual Expression AnalyzeSelectOperation(SelectOperatorType operatorType, IList<Expression> parameters, BuilderContext builderContext)
  1438. {
  1439. // a special case: if we have several SELECT expressions linked together,
  1440. // we maximize the load to the database, since the result must use the same parameters
  1441. // types and count.
  1442. builderContext.QueryContext.MaximumDatabaseLoad = true; // all select expression goes to SQL tier
  1443. var constantExpression = parameters[1] as ConstantExpression;
  1444. QueryProvider queryProvider = constantExpression.Value as QueryProvider;
  1445. if (queryProvider != null)
  1446. {
  1447. // Handle second select first
  1448. BuilderContext newContext = builderContext.NewSisterSelect();
  1449. Expression tableExpression = AnalyzeQueryProvider(queryProvider, newContext);
  1450. BuildSelect(tableExpression, newContext);
  1451. // add the second select select to the chain
  1452. if (newContext.CurrentSelect.NextSelectExpression != null)
  1453. {
  1454. var operand0 = new SubSelectExpression(newContext.CurrentSelect, tableExpression.Type, "source");
  1455. newContext.NewParentSelect();
  1456. newContext.CurrentSelect.Tables.Add(operand0);
  1457. }
  1458. SelectExpression selectToModify = builderContext.CurrentSelect;
  1459. while (selectToModify.NextSelectExpression != null)
  1460. selectToModify = selectToModify.NextSelectExpression;
  1461. selectToModify.NextSelectExpression = newContext.CurrentSelect;
  1462. selectToModify.NextSelectExpressionOperator = operatorType;
  1463. Expression firstSelection = Analyze(parameters[0], builderContext);
  1464. BuildSelect(firstSelection, builderContext);
  1465. return firstSelection;
  1466. }
  1467. return Analyze(parameters[0], builderContext);
  1468. }
  1469. /// <summary>
  1470. /// Analyses InvokeExpression
  1471. /// </summary>
  1472. /// <param name="expression"></param>
  1473. /// <param name="parameters"></param>
  1474. /// <param name="builderContext"></param>
  1475. /// <returns></returns>
  1476. protected virtual Expression AnalyzeInvoke(Expression expression, IList<Expression> parameters,
  1477. BuilderContext builderContext)
  1478. {
  1479. var invocationExpression = (InvocationExpression)expression;
  1480. var lambda = invocationExpression.Expression as LambdaExpression;
  1481. if (lambda != null)
  1482. {
  1483. var localBuilderContext = builderContext.NewQuote();
  1484. //for (int parameterIndex = 0; parameterIndex < lambda.Parameters.Count; parameterIndex++)
  1485. //{
  1486. // var parameter = lambda.Parameters[parameterIndex];
  1487. // localBuilderContext.Parameters[parameter.Name] = Analyze(invocationExpression.Arguments[parameterIndex], builderContext);
  1488. //}
  1489. //return Analyze(lambda, localBuilderContext);
  1490. return Analyze(lambda, invocationExpression.Arguments, localBuilderContext);
  1491. }
  1492. // TODO: see what we must do here
  1493. return expression;
  1494. }
  1495. }
  1496. }