PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ServiceStack.OrmLite/Expressions/SqlExpressionVisitor.cs

https://github.com/shiningrise/ServiceStack.OrmLite
C# | 1229 lines | 992 code | 161 blank | 76 comment | 158 complexity | 2b7cfbd60aca20e92afff33b81bc7414 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Collections.ObjectModel;
  8. using System.Linq.Expressions;
  9. using ServiceStack.Text;
  10. namespace ServiceStack.OrmLite
  11. {
  12. public abstract class SqlExpressionVisitor<T>
  13. {
  14. private Expression<Func<T, bool>> underlyingExpression;
  15. private List<string> orderByProperties = new List<string>();
  16. private string selectExpression = string.Empty;
  17. private string whereExpression;
  18. private string groupBy = string.Empty;
  19. private string havingExpression;
  20. private string orderBy = string.Empty;
  21. IList<string> updateFields = new List<string>();
  22. IList<string> insertFields = new List<string>();
  23. private string sep = string.Empty;
  24. private bool useFieldName = false;
  25. private ModelDefinition modelDef;
  26. public bool PrefixFieldWithTableName {get;set;}
  27. public bool WhereStatementWithoutWhereString { get; set; }
  28. protected string Sep
  29. {
  30. get { return sep; }
  31. }
  32. public SqlExpressionVisitor()
  33. {
  34. modelDef = typeof(T).GetModelDefinition();
  35. PrefixFieldWithTableName = false;
  36. WhereStatementWithoutWhereString = false;
  37. }
  38. /// <summary>
  39. /// Clear select expression. All properties will be selected.
  40. /// </summary>
  41. public virtual SqlExpressionVisitor<T> Select()
  42. {
  43. return Select(string.Empty);
  44. }
  45. /// <summary>
  46. /// set the specified selectExpression.
  47. /// </summary>
  48. /// <param name='selectExpression'>
  49. /// raw Select expression: "Select SomeField1, SomeField2 from SomeTable"
  50. /// </param>
  51. public virtual SqlExpressionVisitor<T> Select(string selectExpression)
  52. {
  53. if (string.IsNullOrEmpty(selectExpression))
  54. {
  55. BuildSelectExpression(string.Empty, false);
  56. }
  57. else
  58. {
  59. this.selectExpression = selectExpression;
  60. }
  61. return this;
  62. }
  63. /// <summary>
  64. /// Fields to be selected.
  65. /// </summary>
  66. /// <param name='fields'>
  67. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  68. /// </param>
  69. /// <typeparam name='TKey'>
  70. /// objectWithProperties
  71. /// </typeparam>
  72. public virtual SqlExpressionVisitor<T> Select<TKey>(Expression<Func<T, TKey>> fields)
  73. {
  74. sep = string.Empty;
  75. useFieldName = true;
  76. BuildSelectExpression(Visit(fields).ToString(), false);
  77. return this;
  78. }
  79. public virtual SqlExpressionVisitor<T> SelectDistinct<TKey>(Expression<Func<T, TKey>> fields)
  80. {
  81. sep = string.Empty;
  82. useFieldName = true;
  83. BuildSelectExpression(Visit(fields).ToString(), true);
  84. return this;
  85. }
  86. public virtual SqlExpressionVisitor<T> Where()
  87. {
  88. if (underlyingExpression != null) underlyingExpression = null; //Where() clears the expression
  89. return Where(string.Empty);
  90. }
  91. public virtual SqlExpressionVisitor<T> Where(string sqlFilter, params object[] filterParams)
  92. {
  93. whereExpression = !string.IsNullOrEmpty(sqlFilter) ? sqlFilter.SqlFormat(filterParams) : string.Empty;
  94. if (!string.IsNullOrEmpty(whereExpression)) whereExpression = (WhereStatementWithoutWhereString ? "" : "WHERE ") + whereExpression;
  95. return this;
  96. }
  97. public virtual SqlExpressionVisitor<T> Where(Expression<Func<T, bool>> predicate)
  98. {
  99. if (predicate != null)
  100. {
  101. And(predicate);
  102. }
  103. else
  104. {
  105. underlyingExpression = null;
  106. whereExpression = string.Empty;
  107. }
  108. return this;
  109. }
  110. public virtual SqlExpressionVisitor<T> And(Expression<Func<T, bool>> predicate)
  111. {
  112. if (predicate != null)
  113. {
  114. if (underlyingExpression == null)
  115. underlyingExpression = predicate;
  116. else
  117. underlyingExpression = underlyingExpression.And(predicate);
  118. ProcessInternalExpression();
  119. }
  120. return this;
  121. }
  122. public virtual SqlExpressionVisitor<T> Or(Expression<Func<T, bool>> predicate)
  123. {
  124. if (predicate != null)
  125. {
  126. if (underlyingExpression == null)
  127. underlyingExpression = predicate;
  128. else
  129. underlyingExpression = underlyingExpression.Or(predicate);
  130. ProcessInternalExpression();
  131. }
  132. return this;
  133. }
  134. private void ProcessInternalExpression()
  135. {
  136. useFieldName = true;
  137. sep = " ";
  138. whereExpression = Visit(underlyingExpression).ToString();
  139. if (!string.IsNullOrEmpty(whereExpression)) whereExpression = (WhereStatementWithoutWhereString ? "" : "WHERE ") + whereExpression;
  140. }
  141. public virtual SqlExpressionVisitor<T> GroupBy()
  142. {
  143. return GroupBy(string.Empty);
  144. }
  145. public virtual SqlExpressionVisitor<T> GroupBy(string groupBy)
  146. {
  147. this.groupBy = groupBy;
  148. return this;
  149. }
  150. public virtual SqlExpressionVisitor<T> GroupBy<TKey>(Expression<Func<T, TKey>> keySelector)
  151. {
  152. sep = string.Empty;
  153. useFieldName = true;
  154. groupBy = Visit(keySelector).ToString();
  155. if (!string.IsNullOrEmpty(groupBy)) groupBy = string.Format("GROUP BY {0}", groupBy);
  156. return this;
  157. }
  158. public virtual SqlExpressionVisitor<T> Having()
  159. {
  160. return Having(string.Empty);
  161. }
  162. public virtual SqlExpressionVisitor<T> Having(string sqlFilter, params object[] filterParams)
  163. {
  164. havingExpression = !string.IsNullOrEmpty(sqlFilter) ? sqlFilter.SqlFormat(filterParams) : string.Empty;
  165. if (!string.IsNullOrEmpty(havingExpression)) havingExpression = "HAVING " + havingExpression;
  166. return this;
  167. }
  168. public virtual SqlExpressionVisitor<T> Having(Expression<Func<T, bool>> predicate)
  169. {
  170. if (predicate != null)
  171. {
  172. useFieldName = true;
  173. sep = " ";
  174. havingExpression = Visit(predicate).ToString();
  175. if (!string.IsNullOrEmpty(havingExpression)) havingExpression = "HAVING " + havingExpression;
  176. }
  177. else
  178. havingExpression = string.Empty;
  179. return this;
  180. }
  181. public virtual SqlExpressionVisitor<T> OrderBy()
  182. {
  183. return OrderBy(string.Empty);
  184. }
  185. public virtual SqlExpressionVisitor<T> OrderBy(string orderBy)
  186. {
  187. orderByProperties.Clear();
  188. this.orderBy = orderBy;
  189. return this;
  190. }
  191. public virtual SqlExpressionVisitor<T> OrderBy<TKey>(Expression<Func<T, TKey>> keySelector)
  192. {
  193. sep = string.Empty;
  194. useFieldName = true;
  195. orderByProperties.Clear();
  196. var property = Visit(keySelector).ToString();
  197. orderByProperties.Add(property + " ASC");
  198. BuildOrderByClauseInternal();
  199. return this;
  200. }
  201. public virtual SqlExpressionVisitor<T> ThenBy<TKey> (Expression<Func<T,TKey>> keySelector)
  202. {
  203. sep = string.Empty;
  204. useFieldName = true;
  205. var property = Visit(keySelector).ToString();
  206. orderByProperties.Add(property + " ASC");
  207. BuildOrderByClauseInternal();
  208. return this;
  209. }
  210. public virtual SqlExpressionVisitor<T> OrderByDescending<TKey>(Expression<Func<T, TKey>> keySelector)
  211. {
  212. sep = string.Empty;
  213. useFieldName = true;
  214. orderByProperties.Clear();
  215. var property = Visit(keySelector).ToString();
  216. orderByProperties.Add(property + " DESC");
  217. BuildOrderByClauseInternal();
  218. return this;
  219. }
  220. public virtual SqlExpressionVisitor<T> ThenByDescending<TKey>(Expression<Func<T, TKey>> keySelector)
  221. {
  222. sep = string.Empty;
  223. useFieldName = true;
  224. var property = Visit(keySelector).ToString();
  225. orderByProperties.Add(property + " DESC");
  226. BuildOrderByClauseInternal();
  227. return this;
  228. }
  229. private void BuildOrderByClauseInternal()
  230. {
  231. if (orderByProperties.Count > 0)
  232. {
  233. orderBy = "ORDER BY ";
  234. foreach(var prop in orderByProperties)
  235. {
  236. orderBy += prop + ",";
  237. }
  238. orderBy = orderBy.TrimEnd(',');
  239. }
  240. else
  241. {
  242. orderBy = null;
  243. }
  244. }
  245. /// <summary>
  246. /// Set the specified offset and rows for SQL Limit clause.
  247. /// </summary>
  248. /// <param name='skip'>
  249. /// Offset of the first row to return. The offset of the initial row is 0
  250. /// </param>
  251. /// <param name='rows'>
  252. /// Number of rows returned by a SELECT statement
  253. /// </param>
  254. public virtual SqlExpressionVisitor<T> Limit(int skip, int rows)
  255. {
  256. Rows = rows;
  257. Skip = skip;
  258. return this;
  259. }
  260. /// <summary>
  261. /// Set the specified rows for Sql Limit clause.
  262. /// </summary>
  263. /// <param name='rows'>
  264. /// Number of rows returned by a SELECT statement
  265. /// </param>
  266. public virtual SqlExpressionVisitor<T> Limit(int rows)
  267. {
  268. Rows = rows;
  269. Skip = 0;
  270. return this;
  271. }
  272. /// <summary>
  273. /// Clear Sql Limit clause
  274. /// </summary>
  275. public virtual SqlExpressionVisitor<T> Limit()
  276. {
  277. Skip = null;
  278. Rows = null;
  279. return this;
  280. }
  281. /// <summary>
  282. /// Fields to be updated.
  283. /// </summary>
  284. /// <param name='updatefields'>
  285. /// IList<string> containing Names of properties to be updated
  286. /// </param>
  287. public virtual SqlExpressionVisitor<T> Update(IList<string> updateFields)
  288. {
  289. this.updateFields = updateFields;
  290. return this;
  291. }
  292. /// <summary>
  293. /// Fields to be updated.
  294. /// </summary>
  295. /// <param name='fields'>
  296. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  297. /// </param>
  298. /// <typeparam name='TKey'>
  299. /// objectWithProperties
  300. /// </typeparam>
  301. public virtual SqlExpressionVisitor<T> Update<TKey>(Expression<Func<T, TKey>> fields)
  302. {
  303. sep = string.Empty;
  304. useFieldName = false;
  305. updateFields = Visit(fields).ToString().Split(',').ToList();
  306. return this;
  307. }
  308. /// <summary>
  309. /// Clear UpdateFields list ( all fields will be updated)
  310. /// </summary>
  311. public virtual SqlExpressionVisitor<T> Update()
  312. {
  313. this.updateFields = new List<string>();
  314. return this;
  315. }
  316. /// <summary>
  317. /// Fields to be inserted.
  318. /// </summary>
  319. /// <param name='fields'>
  320. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  321. /// </param>
  322. /// <typeparam name='TKey'>
  323. /// objectWithProperties
  324. /// </typeparam>
  325. public virtual SqlExpressionVisitor<T> Insert<TKey>(Expression<Func<T, TKey>> fields)
  326. {
  327. sep = string.Empty;
  328. useFieldName = false;
  329. insertFields = Visit(fields).ToString().Split(',').ToList();
  330. return this;
  331. }
  332. /// <summary>
  333. /// fields to be inserted.
  334. /// </summary>
  335. /// <param name='insertFields'>
  336. /// IList&lt;string&gt; containing Names of properties to be inserted
  337. /// </param>
  338. public virtual SqlExpressionVisitor<T> Insert(IList<string> insertFields)
  339. {
  340. this.insertFields = insertFields;
  341. return this;
  342. }
  343. /// <summary>
  344. /// Clear InsertFields list ( all fields will be inserted)
  345. /// </summary>
  346. public virtual SqlExpressionVisitor<T> Insert()
  347. {
  348. this.insertFields = new List<string>();
  349. return this;
  350. }
  351. public virtual string ToDeleteRowStatement()
  352. {
  353. return string.Format("DELETE FROM {0} {1}",
  354. OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef),
  355. WhereExpression);
  356. }
  357. public virtual string ToUpdateStatement(T item, bool excludeDefaults = false)
  358. {
  359. var setFields = new StringBuilder();
  360. var dialectProvider = OrmLiteConfig.DialectProvider;
  361. foreach (var fieldDef in modelDef.FieldDefinitions)
  362. {
  363. if (updateFields.Count > 0 && !updateFields.Contains(fieldDef.Name)) continue; // added
  364. var value = fieldDef.GetValue(item);
  365. if (excludeDefaults && (value == null || value.Equals(value.GetType().GetDefaultValue()))) continue; //GetDefaultValue?
  366. fieldDef.GetQuotedValue(item);
  367. if (setFields.Length > 0) setFields.Append(",");
  368. setFields.AppendFormat("{0} = {1}",
  369. dialectProvider.GetQuotedColumnName(fieldDef.FieldName),
  370. dialectProvider.GetQuotedValue(value, fieldDef.FieldType));
  371. }
  372. return string.Format("UPDATE {0} SET {1} {2}",
  373. dialectProvider.GetQuotedTableName(modelDef), setFields, WhereExpression);
  374. }
  375. public virtual string ToSelectStatement()
  376. {
  377. var sql = new StringBuilder();
  378. sql.Append(SelectExpression);
  379. sql.Append(string.IsNullOrEmpty(WhereExpression) ?
  380. "" :
  381. "\n" + WhereExpression);
  382. sql.Append(string.IsNullOrEmpty(GroupByExpression) ?
  383. "" :
  384. "\n" + GroupByExpression);
  385. sql.Append(string.IsNullOrEmpty(HavingExpression) ?
  386. "" :
  387. "\n" + HavingExpression);
  388. sql.Append(string.IsNullOrEmpty(OrderByExpression) ?
  389. "" :
  390. "\n" + OrderByExpression);
  391. return ApplyPaging(sql.ToString());
  392. }
  393. public virtual string ToCountStatement()
  394. {
  395. return OrmLiteConfig.DialectProvider.ToCountStatement(modelDef.ModelType, WhereExpression, null);
  396. }
  397. public string SelectExpression
  398. {
  399. get
  400. {
  401. if (string.IsNullOrEmpty(selectExpression))
  402. BuildSelectExpression(string.Empty, false);
  403. return selectExpression;
  404. }
  405. set
  406. {
  407. selectExpression = value;
  408. }
  409. }
  410. public string WhereExpression
  411. {
  412. get
  413. {
  414. return whereExpression;
  415. }
  416. set
  417. {
  418. whereExpression = value;
  419. }
  420. }
  421. public string GroupByExpression
  422. {
  423. get
  424. {
  425. return groupBy;
  426. }
  427. set
  428. {
  429. groupBy = value;
  430. }
  431. }
  432. public string HavingExpression
  433. {
  434. get
  435. {
  436. return havingExpression;
  437. }
  438. set
  439. {
  440. havingExpression = value;
  441. }
  442. }
  443. public string OrderByExpression
  444. {
  445. get
  446. {
  447. return orderBy;
  448. }
  449. set
  450. {
  451. orderBy = value;
  452. }
  453. }
  454. public virtual string LimitExpression
  455. {
  456. get
  457. {
  458. if (!Skip.HasValue) return "";
  459. string rows;
  460. if (Rows.HasValue)
  461. {
  462. rows = string.Format(",{0}", Rows.Value);
  463. }
  464. else
  465. {
  466. rows = string.Empty;
  467. }
  468. return string.Format("LIMIT {0}{1}", Skip.Value, rows);
  469. }
  470. }
  471. public int? Rows { get; set; }
  472. public int? Skip { get; set; }
  473. public IList<string> UpdateFields
  474. {
  475. get
  476. {
  477. return updateFields;
  478. }
  479. set
  480. {
  481. updateFields = value;
  482. }
  483. }
  484. public IList<string> InsertFields
  485. {
  486. get
  487. {
  488. return insertFields;
  489. }
  490. set
  491. {
  492. insertFields = value;
  493. }
  494. }
  495. protected internal ModelDefinition ModelDef
  496. {
  497. get
  498. {
  499. return modelDef;
  500. }
  501. set
  502. {
  503. modelDef = value;
  504. }
  505. }
  506. protected internal bool UseFieldName
  507. {
  508. get
  509. {
  510. return useFieldName;
  511. }
  512. set
  513. {
  514. useFieldName = value;
  515. }
  516. }
  517. protected internal virtual object Visit(Expression exp)
  518. {
  519. if (exp == null) return string.Empty;
  520. switch (exp.NodeType)
  521. {
  522. case ExpressionType.Lambda:
  523. return VisitLambda(exp as LambdaExpression);
  524. case ExpressionType.MemberAccess:
  525. return VisitMemberAccess(exp as MemberExpression);
  526. case ExpressionType.Constant:
  527. return VisitConstant(exp as ConstantExpression);
  528. case ExpressionType.Add:
  529. case ExpressionType.AddChecked:
  530. case ExpressionType.Subtract:
  531. case ExpressionType.SubtractChecked:
  532. case ExpressionType.Multiply:
  533. case ExpressionType.MultiplyChecked:
  534. case ExpressionType.Divide:
  535. case ExpressionType.Modulo:
  536. case ExpressionType.And:
  537. case ExpressionType.AndAlso:
  538. case ExpressionType.Or:
  539. case ExpressionType.OrElse:
  540. case ExpressionType.LessThan:
  541. case ExpressionType.LessThanOrEqual:
  542. case ExpressionType.GreaterThan:
  543. case ExpressionType.GreaterThanOrEqual:
  544. case ExpressionType.Equal:
  545. case ExpressionType.NotEqual:
  546. case ExpressionType.Coalesce:
  547. case ExpressionType.ArrayIndex:
  548. case ExpressionType.RightShift:
  549. case ExpressionType.LeftShift:
  550. case ExpressionType.ExclusiveOr:
  551. //return "(" + VisitBinary(exp as BinaryExpression) + ")";
  552. return VisitBinary(exp as BinaryExpression);
  553. case ExpressionType.Negate:
  554. case ExpressionType.NegateChecked:
  555. case ExpressionType.Not:
  556. case ExpressionType.Convert:
  557. case ExpressionType.ConvertChecked:
  558. case ExpressionType.ArrayLength:
  559. case ExpressionType.Quote:
  560. case ExpressionType.TypeAs:
  561. return VisitUnary(exp as UnaryExpression);
  562. case ExpressionType.Parameter:
  563. return VisitParameter(exp as ParameterExpression);
  564. case ExpressionType.Call:
  565. return VisitMethodCall(exp as MethodCallExpression);
  566. case ExpressionType.New:
  567. return VisitNew(exp as NewExpression);
  568. case ExpressionType.NewArrayInit:
  569. case ExpressionType.NewArrayBounds:
  570. return VisitNewArray(exp as NewArrayExpression);
  571. case ExpressionType.MemberInit:
  572. return VisitMemberInit(exp as MemberInitExpression);
  573. default:
  574. return exp.ToString();
  575. }
  576. }
  577. protected virtual object VisitLambda(LambdaExpression lambda)
  578. {
  579. if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
  580. {
  581. MemberExpression m = lambda.Body as MemberExpression;
  582. if (m.Expression != null)
  583. {
  584. string r = VisitMemberAccess(m).ToString();
  585. return string.Format("{0}={1}", r, GetQuotedTrueValue());
  586. }
  587. }
  588. return Visit(lambda.Body);
  589. }
  590. protected virtual object VisitBinary(BinaryExpression b)
  591. {
  592. object left, right;
  593. var operand = BindOperant(b.NodeType); //sep= " " ??
  594. if (operand == "AND" || operand == "OR")
  595. {
  596. var m = b.Left as MemberExpression;
  597. if (m != null && m.Expression != null
  598. && m.Expression.NodeType == ExpressionType.Parameter)
  599. left = new PartialSqlString(string.Format("{0}={1}", VisitMemberAccess(m), GetQuotedTrueValue()));
  600. else
  601. left = Visit(b.Left);
  602. m = b.Right as MemberExpression;
  603. if (m != null && m.Expression != null
  604. && m.Expression.NodeType == ExpressionType.Parameter)
  605. right = new PartialSqlString(string.Format("{0}={1}", VisitMemberAccess(m), GetQuotedTrueValue()));
  606. else
  607. right = Visit(b.Right);
  608. if (left as PartialSqlString == null && right as PartialSqlString == null)
  609. {
  610. var result = Expression.Lambda(b).Compile().DynamicInvoke();
  611. return new PartialSqlString(OrmLiteConfig.DialectProvider.GetQuotedValue(result, result.GetType()));
  612. }
  613. if(left as PartialSqlString == null)
  614. left = ((bool) left) ? GetTrueExpression() : GetFalseExpression();
  615. if (right as PartialSqlString == null)
  616. right = ((bool)right) ? GetTrueExpression() : GetFalseExpression();
  617. }
  618. else
  619. {
  620. left = Visit(b.Left);
  621. right = Visit(b.Right);
  622. if (left as EnumMemberAccess != null && right as PartialSqlString == null)
  623. {
  624. var enumType = ((EnumMemberAccess)left).EnumType;
  625. //enum value was returned by Visit(b.Right)
  626. long numvericVal;
  627. if (Int64.TryParse(right.ToString(), out numvericVal))
  628. right = OrmLiteConfig.DialectProvider.GetQuotedValue(Enum.ToObject(enumType, numvericVal).ToString(),
  629. typeof(string));
  630. else
  631. right = OrmLiteConfig.DialectProvider.GetQuotedValue(right, right.GetType());
  632. }
  633. else if (right as EnumMemberAccess != null && left as PartialSqlString == null)
  634. {
  635. var enumType = ((EnumMemberAccess)right).EnumType;
  636. //enum value was returned by Visit(b.Left)
  637. long numvericVal;
  638. if (Int64.TryParse(left.ToString(), out numvericVal))
  639. left = OrmLiteConfig.DialectProvider.GetQuotedValue(Enum.ToObject(enumType, numvericVal).ToString(),
  640. typeof(string));
  641. else
  642. left = OrmLiteConfig.DialectProvider.GetQuotedValue(left, left.GetType());
  643. }
  644. else if (left as PartialSqlString == null && right as PartialSqlString == null)
  645. {
  646. var result = Expression.Lambda(b).Compile().DynamicInvoke();
  647. return result;
  648. }
  649. else if (left as PartialSqlString == null)
  650. left = OrmLiteConfig.DialectProvider.GetQuotedValue(left, left != null ? left.GetType() : null);
  651. else if (right as PartialSqlString == null)
  652. right = OrmLiteConfig.DialectProvider.GetQuotedValue(right, right != null ? right.GetType() : null);
  653. }
  654. if (operand == "=" && right.ToString().Equals("null", StringComparison.InvariantCultureIgnoreCase)) operand = "is";
  655. else if (operand == "<>" && right.ToString().Equals("null", StringComparison.InvariantCultureIgnoreCase)) operand = "is not";
  656. switch (operand)
  657. {
  658. case "MOD":
  659. case "COALESCE":
  660. return new PartialSqlString(string.Format("{0}({1},{2})", operand, left, right));
  661. default:
  662. return new PartialSqlString("(" + left + sep + operand + sep + right +")");
  663. }
  664. }
  665. protected virtual object VisitMemberAccess(MemberExpression m)
  666. {
  667. if (m.Expression != null
  668. && (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.Convert))
  669. {
  670. var propertyInfo = m.Member as PropertyInfo;
  671. if (propertyInfo.PropertyType.IsEnum)
  672. return new EnumMemberAccess((PrefixFieldWithTableName ? OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef.ModelName) + "." : "") + GetQuotedColumnName(m.Member.Name), propertyInfo.PropertyType);
  673. return new PartialSqlString((PrefixFieldWithTableName ? OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef.ModelName)+"." : "") + GetQuotedColumnName(m.Member.Name));
  674. }
  675. var member = Expression.Convert(m, typeof(object));
  676. var lambda = Expression.Lambda<Func<object>>(member);
  677. var getter = lambda.Compile();
  678. return getter();
  679. }
  680. protected virtual object VisitMemberInit(MemberInitExpression exp)
  681. {
  682. return Expression.Lambda(exp).Compile().DynamicInvoke();
  683. }
  684. protected virtual object VisitNew(NewExpression nex)
  685. {
  686. // TODO : check !
  687. var member = Expression.Convert(nex, typeof(object));
  688. var lambda = Expression.Lambda<Func<object>>(member);
  689. try
  690. {
  691. var getter = lambda.Compile();
  692. return getter();
  693. }
  694. catch (System.InvalidOperationException)
  695. { // FieldName ?
  696. List<Object> exprs = VisitExpressionList(nex.Arguments);
  697. StringBuilder r = new StringBuilder();
  698. foreach (Object e in exprs)
  699. {
  700. r.AppendFormat("{0}{1}",
  701. r.Length > 0 ? "," : "",
  702. e);
  703. }
  704. return r.ToString();
  705. }
  706. }
  707. protected virtual object VisitParameter(ParameterExpression p)
  708. {
  709. return p.Name;
  710. }
  711. public bool IsParameterized { get; set; }
  712. public Dictionary<string, object> Params = new Dictionary<string, object>();
  713. int paramCounter = 0;
  714. protected virtual object VisitConstant(ConstantExpression c)
  715. {
  716. if (c.Value == null)
  717. return new PartialSqlString("null");
  718. if (!IsParameterized)
  719. {
  720. return c.Value;
  721. }
  722. else
  723. {
  724. string paramPlaceholder = OrmLiteConfig.DialectProvider.ParamString + paramCounter++;
  725. Params.Add(paramPlaceholder, c.Value);
  726. return new PartialSqlString(paramPlaceholder);
  727. }
  728. }
  729. protected virtual object VisitUnary(UnaryExpression u)
  730. {
  731. switch (u.NodeType)
  732. {
  733. case ExpressionType.Not:
  734. var o = Visit(u.Operand);
  735. if (o as PartialSqlString == null)
  736. return !((bool)o);
  737. if (IsFieldName(o))
  738. o = o + "=" + GetQuotedTrueValue();
  739. return new PartialSqlString("NOT (" + o + ")");
  740. case ExpressionType.Convert:
  741. if (u.Method != null)
  742. return Expression.Lambda(u).Compile().DynamicInvoke();
  743. break;
  744. }
  745. return Visit(u.Operand);
  746. }
  747. private bool IsColumnAccess(MethodCallExpression m)
  748. {
  749. if (m.Object != null && m.Object as MethodCallExpression != null)
  750. return IsColumnAccess(m.Object as MethodCallExpression);
  751. var exp = m.Object as MemberExpression;
  752. return exp != null
  753. && exp.Expression != null
  754. && exp.Expression.Type == typeof(T)
  755. && exp.Expression.NodeType == ExpressionType.Parameter;
  756. }
  757. protected virtual object VisitMethodCall(MethodCallExpression m)
  758. {
  759. if (m.Method.DeclaringType == typeof(Sql))
  760. return VisitSqlMethodCall(m);
  761. if (IsArrayMethod(m))
  762. return VisitArrayMethodCall(m);
  763. if (IsColumnAccess(m))
  764. return VisitColumnAccessMethod(m);
  765. return Expression.Lambda(m).Compile().DynamicInvoke();
  766. }
  767. protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
  768. {
  769. List<Object> list = new List<Object>();
  770. for (int i = 0, n = original.Count; i < n; i++)
  771. {
  772. if (original[i].NodeType == ExpressionType.NewArrayInit ||
  773. original[i].NodeType == ExpressionType.NewArrayBounds)
  774. {
  775. list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
  776. }
  777. else
  778. list.Add(Visit(original[i]));
  779. }
  780. return list;
  781. }
  782. protected virtual object VisitNewArray(NewArrayExpression na)
  783. {
  784. List<Object> exprs = VisitExpressionList(na.Expressions);
  785. StringBuilder r = new StringBuilder();
  786. foreach (Object e in exprs)
  787. {
  788. r.Append(r.Length > 0 ? "," + e : e);
  789. }
  790. return r.ToString();
  791. }
  792. protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
  793. {
  794. List<Object> exprs = VisitExpressionList(na.Expressions);
  795. return exprs;
  796. }
  797. protected virtual string BindOperant(ExpressionType e)
  798. {
  799. switch (e)
  800. {
  801. case ExpressionType.Equal:
  802. return "=";
  803. case ExpressionType.NotEqual:
  804. return "<>";
  805. case ExpressionType.GreaterThan:
  806. return ">";
  807. case ExpressionType.GreaterThanOrEqual:
  808. return ">=";
  809. case ExpressionType.LessThan:
  810. return "<";
  811. case ExpressionType.LessThanOrEqual:
  812. return "<=";
  813. case ExpressionType.AndAlso:
  814. return "AND";
  815. case ExpressionType.OrElse:
  816. return "OR";
  817. case ExpressionType.Add:
  818. return "+";
  819. case ExpressionType.Subtract:
  820. return "-";
  821. case ExpressionType.Multiply:
  822. return "*";
  823. case ExpressionType.Divide:
  824. return "/";
  825. case ExpressionType.Modulo:
  826. return "MOD";
  827. case ExpressionType.Coalesce:
  828. return "COALESCE";
  829. default:
  830. return e.ToString();
  831. }
  832. }
  833. protected virtual string GetQuotedColumnName(string memberName)
  834. {
  835. if (useFieldName)
  836. {
  837. FieldDefinition fd = modelDef.FieldDefinitions.FirstOrDefault(x => x.Name == memberName);
  838. string fn = fd != default(FieldDefinition) ? fd.FieldName : memberName;
  839. return OrmLiteConfig.DialectProvider.GetQuotedColumnName(fn);
  840. }
  841. else
  842. {
  843. return memberName;
  844. }
  845. }
  846. protected string RemoveQuoteFromAlias(string exp)
  847. {
  848. if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
  849. &&
  850. (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
  851. {
  852. exp = exp.Remove(0, 1);
  853. exp = exp.Remove(exp.Length - 1, 1);
  854. }
  855. return exp;
  856. }
  857. protected bool IsFieldName(object quotedExp)
  858. {
  859. FieldDefinition fd =
  860. modelDef.FieldDefinitions.
  861. FirstOrDefault(x =>
  862. OrmLiteConfig.DialectProvider.
  863. GetQuotedColumnName(x.FieldName) == quotedExp.ToString());
  864. return (fd != default(FieldDefinition));
  865. }
  866. protected object GetTrueExpression()
  867. {
  868. return new PartialSqlString(string.Format("({0}={1})", GetQuotedTrueValue(), GetQuotedTrueValue()));
  869. }
  870. protected object GetFalseExpression()
  871. {
  872. return new PartialSqlString(string.Format("({0}={1})", GetQuotedTrueValue(), GetQuotedFalseValue()));
  873. }
  874. protected static object GetQuotedTrueValue()
  875. {
  876. return new PartialSqlString(OrmLiteConfig.DialectProvider.GetQuotedValue(true, typeof (bool)));
  877. }
  878. protected static object GetQuotedFalseValue()
  879. {
  880. return new PartialSqlString(OrmLiteConfig.DialectProvider.GetQuotedValue(false, typeof(bool)));
  881. }
  882. private void BuildSelectExpression(string fields, bool distinct)
  883. {
  884. selectExpression = string.Format("SELECT {0}{1} \nFROM {2}",
  885. (distinct ? "DISTINCT " : ""),
  886. (string.IsNullOrEmpty(fields) ?
  887. OrmLiteConfig.DialectProvider.GetColumnNames(modelDef) :
  888. fields),
  889. OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef));
  890. }
  891. public IList<string> GetAllFields()
  892. {
  893. return modelDef.FieldDefinitions.ConvertAll(r => r.Name);
  894. }
  895. protected virtual string ApplyPaging(string sql)
  896. {
  897. sql = sql + (string.IsNullOrEmpty(LimitExpression) ? "" :"\n" + LimitExpression);
  898. return sql;
  899. }
  900. private bool IsArrayMethod(MethodCallExpression m)
  901. {
  902. if (m.Object == null && m.Method.Name == "Contains")
  903. {
  904. if (m.Arguments.Count == 2)
  905. return true;
  906. }
  907. return false;
  908. }
  909. protected virtual object VisitArrayMethodCall(MethodCallExpression m)
  910. {
  911. string statement;
  912. switch (m.Method.Name)
  913. {
  914. case "Contains":
  915. List<Object> args = this.VisitExpressionList(m.Arguments);
  916. object quotedColName = args[1];
  917. var memberExpr = m.Arguments[0];
  918. if (memberExpr.NodeType == ExpressionType.MemberAccess)
  919. memberExpr = (m.Arguments[0] as MemberExpression);
  920. var member = Expression.Convert(memberExpr, typeof(object));
  921. var lambda = Expression.Lambda<Func<object>>(member);
  922. var getter = lambda.Compile();
  923. var inArgs = Sql.Flatten(getter() as IEnumerable);
  924. StringBuilder sIn = new StringBuilder();
  925. foreach (Object e in inArgs)
  926. {
  927. sIn.AppendFormat("{0}{1}",
  928. sIn.Length > 0 ? "," : "",
  929. OrmLiteConfig.DialectProvider.GetQuotedValue(e, e.GetType()));
  930. }
  931. statement = string.Format("{0} {1} ({2})", quotedColName, "In", sIn.ToString());
  932. break;
  933. default:
  934. throw new NotSupportedException();
  935. }
  936. return new PartialSqlString(statement);
  937. }
  938. protected virtual object VisitSqlMethodCall(MethodCallExpression m)
  939. {
  940. List<Object> args = this.VisitExpressionList(m.Arguments);
  941. object quotedColName = args[0];
  942. args.RemoveAt(0);
  943. string statement;
  944. switch (m.Method.Name)
  945. {
  946. case "In":
  947. var member = Expression.Convert(m.Arguments[1], typeof(object));
  948. var lambda = Expression.Lambda<Func<object>>(member);
  949. var getter = lambda.Compile();
  950. var inArgs = Sql.Flatten(getter() as IEnumerable);
  951. StringBuilder sIn = new StringBuilder();
  952. foreach (Object e in inArgs)
  953. {
  954. if (!typeof(ICollection).IsAssignableFrom(e.GetType()))
  955. {
  956. sIn.AppendFormat("{0}{1}",
  957. sIn.Length > 0 ? "," : "",
  958. OrmLiteConfig.DialectProvider.GetQuotedValue(e, e.GetType()));
  959. }
  960. else
  961. {
  962. var listArgs = e as ICollection;
  963. foreach (Object el in listArgs)
  964. {
  965. sIn.AppendFormat("{0}{1}",
  966. sIn.Length > 0 ? "," : "",
  967. OrmLiteConfig.DialectProvider.GetQuotedValue(el, el.GetType()));
  968. }
  969. }
  970. }
  971. statement = string.Format("{0} {1} ({2})", quotedColName, m.Method.Name, sIn.ToString());
  972. break;
  973. case "Desc":
  974. statement = string.Format("{0} DESC", quotedColName);
  975. break;
  976. case "As":
  977. statement = string.Format("{0} As {1}", quotedColName,
  978. OrmLiteConfig.DialectProvider.GetQuotedColumnName(RemoveQuoteFromAlias(args[0].ToString())));
  979. break;
  980. case "Sum":
  981. case "Count":
  982. case "Min":
  983. case "Max":
  984. case "Avg":
  985. statement = string.Format("{0}({1}{2})",
  986. m.Method.Name,
  987. quotedColName,
  988. args.Count == 1 ? string.Format(",{0}", args[0]) : "");
  989. break;
  990. default:
  991. throw new NotSupportedException();
  992. }
  993. return new PartialSqlString(statement);
  994. }
  995. protected virtual object VisitColumnAccessMethod(MethodCallExpression m)
  996. {
  997. List<Object> args = this.VisitExpressionList(m.Arguments);
  998. var quotedColName = Visit(m.Object);
  999. var statement = "";
  1000. switch (m.Method.Name)
  1001. {
  1002. case "Trim":
  1003. statement = string.Format("ltrim(rtrim({0}))", quotedColName);
  1004. break;
  1005. case "LTrim":
  1006. statement = string.Format("ltrim({0})", quotedColName);
  1007. break;
  1008. case "RTrim":
  1009. statement = string.Format("rtrim({0})", quotedColName);
  1010. break;
  1011. case "ToUpper":
  1012. statement = string.Format("upper({0})", quotedColName);
  1013. break;
  1014. case "ToLower":
  1015. statement = string.Format("lower({0})", quotedColName);
  1016. break;
  1017. case "StartsWith":
  1018. statement = string.Format("upper({0}) like {1} ", quotedColName, OrmLiteConfig.DialectProvider.GetQuotedParam(args[0].ToString().ToUpper() + "%"));
  1019. break;
  1020. case "EndsWith":
  1021. statement = string.Format("upper({0}) like {1}", quotedColName, OrmLiteConfig.DialectProvider.GetQuotedParam("%" + args[0].ToString().ToUpper()));
  1022. break;
  1023. case "Contains":
  1024. statement = string.Format("upper({0}) like {1}", quotedColName, OrmLiteConfig.DialectProvider.GetQuotedParam("%" + args[0].ToString().ToUpper() + "%"));
  1025. break;
  1026. case "Substring":
  1027. var startIndex = Int32.Parse(args[0].ToString()) + 1;
  1028. if (args.Count == 2)
  1029. {
  1030. var length = Int32.Parse(args[1].ToString());
  1031. statement = string.Format("substring({0} from {1} for {2})",
  1032. quotedColName,
  1033. startIndex,
  1034. length);
  1035. }
  1036. else
  1037. statement = string.Format("substring({0} from {1})",
  1038. quotedColName,
  1039. startIndex);
  1040. break;
  1041. default:
  1042. throw new NotSupportedException();
  1043. }
  1044. return new PartialSqlString(statement);
  1045. }
  1046. }
  1047. public class PartialSqlString
  1048. {
  1049. public PartialSqlString(string text)
  1050. {
  1051. Text = text;
  1052. }
  1053. public string Text { get; set; }
  1054. public override string ToString()
  1055. {
  1056. return Text;
  1057. }
  1058. }
  1059. public class EnumMemberAccess : PartialSqlString
  1060. {
  1061. public EnumMemberAccess(string text, Type enumType) : base(text)
  1062. {
  1063. if (!enumType.IsEnum) throw new ArgumentException("Type not valid", "enumType");
  1064. EnumType = enumType;
  1065. }
  1066. public Type EnumType { get; private set; }
  1067. }
  1068. }