/src/NPoco/Expressions/SqlExpression.cs

https://github.com/dengweiwen/NPoco · C# · 1603 lines · 1204 code · 206 blank · 193 comment · 248 complexity · 39cbba8ce4167999fb96499d6138d8c2 MD5 · raw file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using System.Text;
  8. using System.Collections.ObjectModel;
  9. using System.Linq.Expressions;
  10. using NPoco.Linq;
  11. namespace NPoco.Expressions
  12. {
  13. public class OrderByMember
  14. {
  15. public Type EntityType { get; set; }
  16. public PocoColumn PocoColumn { get; set; }
  17. public string AscDesc { get; set; }
  18. }
  19. public class SelectMember : IEquatable<SelectMember>
  20. {
  21. public Type EntityType { get; set; }
  22. public string SelectSql { get; set; }
  23. public PocoColumn PocoColumn { get; set; }
  24. public bool Equals(SelectMember other)
  25. {
  26. if (ReferenceEquals(null, other)) return false;
  27. if (ReferenceEquals(this, other)) return true;
  28. return Equals(EntityType, other.EntityType) && Equals(PocoColumn, other.PocoColumn);
  29. }
  30. public override bool Equals(object obj)
  31. {
  32. if (ReferenceEquals(null, obj)) return false;
  33. if (ReferenceEquals(this, obj)) return true;
  34. if (obj.GetType() != this.GetType()) return false;
  35. return Equals((SelectMember) obj);
  36. }
  37. public override int GetHashCode()
  38. {
  39. unchecked
  40. {
  41. return ((EntityType != null ? EntityType.GetHashCode() : 0)*397) ^ (PocoColumn != null ? PocoColumn.GetHashCode() : 0);
  42. }
  43. }
  44. }
  45. public class GeneralMember
  46. {
  47. public Type EntityType { get; set; }
  48. public PocoColumn PocoColumn { get; set; }
  49. }
  50. public interface ISqlExpression
  51. {
  52. List<OrderByMember> OrderByMembers { get; }
  53. int? Rows { get; }
  54. int? Skip { get; }
  55. string WhereSql { get; }
  56. object[] Params { get; }
  57. Type Type { get; }
  58. List<SelectMember> SelectMembers { get; }
  59. List<GeneralMember> GeneralMembers { get; }
  60. string ApplyPaging(string sql, IEnumerable<PocoColumn> columns);
  61. }
  62. public abstract class SqlExpression<T> : ISqlExpression
  63. {
  64. private Expression<Func<T, bool>> underlyingExpression;
  65. private List<string> orderByProperties = new List<string>();
  66. private List<OrderByMember> orderByMembers = new List<OrderByMember>();
  67. private List<SelectMember> selectMembers = new List<SelectMember>();
  68. private List<GeneralMember> generalMembers = new List<GeneralMember>();
  69. private string selectExpression = string.Empty;
  70. private string whereExpression;
  71. private string groupBy = string.Empty;
  72. private string havingExpression;
  73. private string orderBy = string.Empty;
  74. List<OrderByMember> ISqlExpression.OrderByMembers { get { return orderByMembers; } }
  75. List<SelectMember> ISqlExpression.SelectMembers { get { return selectMembers; } }
  76. List<GeneralMember> ISqlExpression.GeneralMembers { get { return generalMembers; } }
  77. string ISqlExpression.WhereSql { get { return whereExpression; } }
  78. int? ISqlExpression.Rows { get { return Rows; } }
  79. int? ISqlExpression.Skip { get { return Skip; } }
  80. Type ISqlExpression.Type { get { return _type; } }
  81. object[] ISqlExpression.Params { get { return Context.Params; } }
  82. string ISqlExpression.ApplyPaging(string sql, IEnumerable<PocoColumn> columns)
  83. {
  84. return ApplyPaging(sql, columns);
  85. }
  86. private string sep = string.Empty;
  87. private PocoData modelDef;
  88. private readonly IDatabase _database;
  89. private readonly DatabaseType _databaseType;
  90. private bool PrefixFieldWithTableName { get; set; }
  91. private bool WhereStatementWithoutWhereString { get; set; }
  92. private Type _type { get; set; }
  93. public SqlExpression(IDatabase database, bool prefixTableName)
  94. {
  95. _type = typeof(T);
  96. modelDef = database.PocoDataFactory.ForType(typeof(T));
  97. _database = database;
  98. _databaseType = database.DatabaseType;
  99. PrefixFieldWithTableName = prefixTableName;
  100. WhereStatementWithoutWhereString = false;
  101. paramPrefix = "@";
  102. Context = new SqlExpressionContext(this);
  103. }
  104. public class SqlExpressionContext
  105. {
  106. private readonly SqlExpression<T> _expression;
  107. public SqlExpressionContext(SqlExpression<T> expression)
  108. {
  109. _expression = expression;
  110. UpdateFields = new List<string>();
  111. }
  112. public List<string> UpdateFields { get; set; }
  113. public object[] Params { get { return _expression._params.ToArray(); } }
  114. public virtual string ToDeleteStatement()
  115. {
  116. return _expression.ToDeleteStatement();
  117. }
  118. public virtual string ToUpdateStatement(T item)
  119. {
  120. return _expression.ToUpdateStatement(item, false);
  121. }
  122. public virtual string ToUpdateStatement(T item, bool excludeDefaults)
  123. {
  124. return _expression.ToUpdateStatement(item, excludeDefaults);
  125. }
  126. public virtual string ToUpdateStatement(T item, bool excludeDefaults, bool allFields)
  127. {
  128. if (allFields)
  129. _expression.generalMembers = _expression.GetAllMembers().Select(x => new GeneralMember { EntityType = typeof(T), PocoColumn = x }).ToList();
  130. return _expression.ToUpdateStatement(item, excludeDefaults);
  131. }
  132. public string ToWhereStatement()
  133. {
  134. return _expression.ToWhereStatement();
  135. }
  136. public virtual string ToSelectStatement()
  137. {
  138. return ToSelectStatement(true);
  139. }
  140. public virtual string ToSelectStatement(bool applyPaging)
  141. {
  142. return _expression.ToSelectStatement(applyPaging);
  143. }
  144. }
  145. /// <summary>
  146. /// Clear select expression. All properties will be selected.
  147. /// </summary>
  148. //public virtual SqlExpression<T> Select()
  149. //{
  150. // return Select(string.Empty);
  151. //}
  152. /// <summary>
  153. /// set the specified selectExpression.
  154. /// </summary>
  155. /// <param name='selectExpression'>
  156. /// raw Select expression: "Select SomeField1, SomeField2 from SomeTable"
  157. /// </param>
  158. //public virtual SqlExpression<T> Select(string selectExpression)
  159. //{
  160. // if (!string.IsNullOrEmpty(selectExpression))
  161. // {
  162. // this.selectExpression = selectExpression;
  163. // }
  164. // return this;
  165. //}
  166. /// <summary>
  167. /// Fields to be selected.
  168. /// </summary>
  169. /// <param name='fields'>
  170. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  171. /// </param>
  172. /// <typeparam name='TKey'>
  173. /// objectWithProperties
  174. /// </typeparam>
  175. public virtual SqlExpression<T> Select<TKey>(Expression<Func<T, TKey>> fields)
  176. {
  177. sep = string.Empty;
  178. selectMembers.Clear();
  179. Visit(fields);
  180. return this;
  181. }
  182. public virtual List<SelectMember> SelectProjection<TKey>(Expression<Func<T, TKey>> fields)
  183. {
  184. sep = string.Empty;
  185. selectMembers.Clear();
  186. _projection = true;
  187. Visit(fields);
  188. _projection = false;
  189. var proj = new List<SelectMember>(selectMembers.Union(generalMembers.Select(x=>new SelectMember() { EntityType = x.EntityType, PocoColumn = x.PocoColumn, SelectSql = _databaseType.EscapeSqlIdentifier(x.PocoColumn.AutoAlias) })));
  190. selectMembers.Clear();
  191. return proj;
  192. }
  193. public virtual SqlExpression<T> SelectDistinct<TKey>(Expression<Func<T, TKey>> fields)
  194. {
  195. sep = string.Empty;
  196. selectMembers.Clear();
  197. var exp = PartialEvaluator.Eval(fields, CanBeEvaluatedLocally);
  198. Visit(exp);
  199. return this;
  200. }
  201. //public virtual SqlExpression<T> Where()
  202. //{
  203. // if (underlyingExpression != null) underlyingExpression = null; //Where() clears the expression
  204. // return Where(string.Empty);
  205. //}
  206. public virtual SqlExpression<T> Where(string sqlFilter, params object[] filterParams)
  207. {
  208. whereExpression = !string.IsNullOrEmpty(sqlFilter) ? sqlFilter : string.Empty;
  209. foreach (var filterParam in filterParams)
  210. {
  211. CreateParam(filterParam);
  212. }
  213. if (!string.IsNullOrEmpty(whereExpression)) whereExpression = (WhereStatementWithoutWhereString ? "" : "WHERE ") + whereExpression;
  214. return this;
  215. }
  216. public string On<T2>(Expression<Func<T, T2, bool>> predicate)
  217. {
  218. sep = " ";
  219. var onSql = Visit(predicate).ToString();
  220. return onSql;
  221. }
  222. public virtual SqlExpression<T> Where(Expression<Func<T, bool>> predicate)
  223. {
  224. if (predicate != null)
  225. {
  226. And(predicate);
  227. }
  228. else
  229. {
  230. underlyingExpression = null;
  231. whereExpression = string.Empty;
  232. }
  233. return this;
  234. }
  235. protected virtual SqlExpression<T> And(Expression<Func<T, bool>> predicate)
  236. {
  237. if (predicate != null)
  238. {
  239. if (underlyingExpression == null)
  240. underlyingExpression = predicate;
  241. else
  242. underlyingExpression = underlyingExpression.And(predicate);
  243. ProcessInternalExpression();
  244. }
  245. return this;
  246. }
  247. //public virtual SqlExpression<T> Or(Expression<Func<T, bool>> predicate)
  248. //{
  249. // if (predicate != null)
  250. // {
  251. // if (underlyingExpression == null)
  252. // underlyingExpression = predicate;
  253. // else
  254. // underlyingExpression = underlyingExpression.Or(predicate);
  255. // ProcessInternalExpression();
  256. // }
  257. // return this;
  258. //}
  259. private void ProcessInternalExpression()
  260. {
  261. sep = " ";
  262. var exp = PartialEvaluator.Eval(underlyingExpression, CanBeEvaluatedLocally);
  263. whereExpression = Visit(exp).ToString();
  264. if (!string.IsNullOrEmpty(whereExpression)) whereExpression = (WhereStatementWithoutWhereString ? "" : "WHERE ") + whereExpression;
  265. }
  266. private bool CanBeEvaluatedLocally(Expression expression)
  267. {
  268. // any operation on a query can't be done locally
  269. ConstantExpression cex = expression as ConstantExpression;
  270. if (cex != null)
  271. {
  272. IQueryable query = cex.Value as IQueryable;
  273. if (query != null && query.Provider == this)
  274. return false;
  275. }
  276. MethodCallExpression mc = expression as MethodCallExpression;
  277. if (mc != null &&
  278. (mc.Method.DeclaringType == typeof(Enumerable) ||
  279. mc.Method.DeclaringType == typeof(Queryable)))
  280. {
  281. return false;
  282. }
  283. if (expression.NodeType == ExpressionType.Convert &&
  284. expression.Type == typeof(object))
  285. return true;
  286. return expression.NodeType != ExpressionType.Parameter &&
  287. expression.NodeType != ExpressionType.Lambda;
  288. }
  289. //public virtual SqlExpression<T> GroupBy()
  290. //{
  291. // return GroupBy(string.Empty);
  292. //}
  293. //public virtual SqlExpression<T> GroupBy(string groupBy)
  294. //{
  295. // this.groupBy = groupBy;
  296. // return this;
  297. //}
  298. public virtual SqlExpression<T> GroupBy<TKey>(Expression<Func<T, TKey>> keySelector)
  299. {
  300. sep = string.Empty;
  301. groupBy = Visit(keySelector).ToString();
  302. if (!string.IsNullOrEmpty(groupBy)) groupBy = string.Format("GROUP BY {0}", groupBy);
  303. return this;
  304. }
  305. //public virtual SqlExpression<T> Having()
  306. //{
  307. // return Having(string.Empty);
  308. //}
  309. //public virtual SqlExpression<T> Having(string sqlFilter, params object[] filterParams)
  310. //{
  311. // havingExpression = !string.IsNullOrEmpty(sqlFilter) ? sqlFilter : string.Empty;
  312. // foreach (var filterParam in filterParams)
  313. // {
  314. // CreateParam(filterParam);
  315. // }
  316. // if (!string.IsNullOrEmpty(havingExpression)) havingExpression = "HAVING " + havingExpression;
  317. // return this;
  318. //}
  319. //public virtual SqlExpression<T> Having(Expression<Func<T, bool>> predicate)
  320. //{
  321. // if (predicate != null)
  322. // {
  323. // sep = " ";
  324. // havingExpression = Visit(predicate).ToString();
  325. // if (!string.IsNullOrEmpty(havingExpression)) havingExpression = "HAVING " + havingExpression;
  326. // }
  327. // else
  328. // havingExpression = string.Empty;
  329. // return this;
  330. //}
  331. //public virtual SqlExpression<T> OrderBy()
  332. //{
  333. // return OrderBy(string.Empty);
  334. //}
  335. //public virtual SqlExpression<T> OrderBy(string orderBy)
  336. //{
  337. // orderByProperties.Clear();
  338. // this.orderBy = orderBy;
  339. // return this;
  340. //}
  341. public virtual SqlExpression<T> OrderBy<TKey>(Expression<Func<T, TKey>> keySelector)
  342. {
  343. sep = string.Empty;
  344. orderByProperties.Clear();
  345. orderByMembers.Clear();
  346. generalMembers.Clear();
  347. var memberAccess = (MemberAccessString)Visit(keySelector);
  348. orderByProperties.Add(memberAccess + " ASC");
  349. orderByMembers.Add(new OrderByMember { AscDesc = "ASC", PocoColumn = memberAccess.PocoColumn, EntityType = memberAccess.Type});
  350. generalMembers.Clear();
  351. BuildOrderByClauseInternal();
  352. return this;
  353. }
  354. public virtual SqlExpression<T> ThenBy<TKey>(Expression<Func<T, TKey>> keySelector)
  355. {
  356. sep = string.Empty;
  357. generalMembers.Clear();
  358. var memberAccess = (MemberAccessString)Visit(keySelector);
  359. orderByProperties.Add(memberAccess + " ASC");
  360. orderByMembers.Add(new OrderByMember { AscDesc = "ASC", PocoColumn = memberAccess.PocoColumn, EntityType = memberAccess.Type });
  361. generalMembers.Clear();
  362. BuildOrderByClauseInternal();
  363. return this;
  364. }
  365. public virtual SqlExpression<T> OrderByDescending<TKey>(Expression<Func<T, TKey>> keySelector)
  366. {
  367. sep = string.Empty;
  368. orderByProperties.Clear();
  369. orderByMembers.Clear();
  370. generalMembers.Clear();
  371. var memberAccess = (MemberAccessString)Visit(keySelector);
  372. orderByProperties.Add(memberAccess + " DESC");
  373. orderByMembers.Add(new OrderByMember { AscDesc = "DESC", PocoColumn = memberAccess.PocoColumn, EntityType = memberAccess.Type });
  374. generalMembers.Clear();
  375. BuildOrderByClauseInternal();
  376. return this;
  377. }
  378. public virtual SqlExpression<T> ThenByDescending<TKey>(Expression<Func<T, TKey>> keySelector)
  379. {
  380. sep = string.Empty;
  381. generalMembers.Clear();
  382. var memberAccess = (MemberAccessString)Visit(keySelector);
  383. orderByProperties.Add(memberAccess + " DESC");
  384. orderByMembers.Add(new OrderByMember { AscDesc = "DESC", PocoColumn = memberAccess.PocoColumn, EntityType = memberAccess.Type });
  385. generalMembers.Clear();
  386. BuildOrderByClauseInternal();
  387. return this;
  388. }
  389. private void BuildOrderByClauseInternal()
  390. {
  391. if (orderByMembers.Count > 0)
  392. {
  393. orderBy = "ORDER BY " + string.Join(", ", orderByMembers.Select(x => (PrefixFieldWithTableName ? _databaseType.EscapeSqlIdentifier(x.PocoColumn.AutoAlias) : _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName)) + " " + x.AscDesc).ToArray());
  394. }
  395. else
  396. {
  397. orderBy = null;
  398. }
  399. }
  400. /// <summary>
  401. /// Set the specified offset and rows for SQL Limit clause.
  402. /// </summary>
  403. /// <param name='skip'>
  404. /// Offset of the first row to return. The offset of the initial row is 0
  405. /// </param>
  406. /// <param name='rows'>
  407. /// Number of rows returned by a SELECT statement
  408. /// </param>
  409. public virtual SqlExpression<T> Limit(int skip, int rows)
  410. {
  411. Rows = rows;
  412. Skip = skip;
  413. return this;
  414. }
  415. /// <summary>
  416. /// Set the specified rows for Sql Limit clause.
  417. /// </summary>
  418. /// <param name='rows'>
  419. /// Number of rows returned by a SELECT statement
  420. /// </param>
  421. public virtual SqlExpression<T> Limit(int rows)
  422. {
  423. Rows = rows;
  424. Skip = 0;
  425. return this;
  426. }
  427. /// <summary>
  428. /// Clear Sql Limit clause
  429. /// </summary>
  430. //public virtual SqlExpression<T> Limit()
  431. //{
  432. // Skip = null;
  433. // Rows = null;
  434. // return this;
  435. //}
  436. /// <summary>
  437. /// Fields to be updated.
  438. /// </summary>
  439. /// <param name='updatefields'>
  440. /// IList<string> containing Names of properties to be updated
  441. /// </param>
  442. //public virtual SqlExpression<T> Update(IList<string> updateFields)
  443. //{
  444. // this.updateFields = updateFields;
  445. // return this;
  446. //}
  447. /// <summary>
  448. /// Fields to be updated.
  449. /// </summary>
  450. /// <param name='fields'>
  451. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  452. /// </param>
  453. /// <typeparam name='TKey'>
  454. /// objectWithProperties
  455. /// </typeparam>
  456. public virtual SqlExpression<T> Update<TKey>(Expression<Func<T, TKey>> fields)
  457. {
  458. sep = string.Empty;
  459. generalMembers.Clear();
  460. Visit(fields);
  461. Context.UpdateFields = new List<string>(generalMembers.Select(x => x.PocoColumn.ColumnName));
  462. generalMembers.Clear();
  463. return this;
  464. }
  465. /// <summary>
  466. /// Clear UpdateFields list ( all fields will be updated)
  467. /// </summary>
  468. //public virtual SqlExpression<T> Update()
  469. //{
  470. // this.updateFields = new List<string>();
  471. // return this;
  472. //}
  473. /// <summary>
  474. /// Fields to be inserted.
  475. /// </summary>
  476. /// <param name='fields'>
  477. /// x=> x.SomeProperty1 or x=> new{ x.SomeProperty1, x.SomeProperty2}
  478. /// </param>
  479. /// <typeparam name='TKey'>
  480. /// objectWithProperties
  481. /// </typeparam>
  482. //public virtual SqlExpression<T> Insert<TKey>(Expression<Func<T, TKey>> fields)
  483. //{
  484. // sep = string.Empty;
  485. // Context.InsertFields = Visit(fields).ToString().Split(',').ToList();
  486. // return this;
  487. //}
  488. /// <summary>
  489. /// fields to be inserted.
  490. /// </summary>
  491. /// <param name='insertFields'>
  492. /// IList&lt;string&gt; containing Names of properties to be inserted
  493. /// </param>
  494. //public virtual SqlExpression<T> Insert(IList<string> insertFields)
  495. //{
  496. // this.insertFields = insertFields;
  497. // return this;
  498. //}
  499. /// <summary>
  500. /// Clear InsertFields list ( all fields will be inserted)
  501. /// </summary>
  502. //public virtual SqlExpression<T> Insert()
  503. //{
  504. // this.insertFields = new List<string>();
  505. // return this;
  506. //}
  507. protected virtual string ToDeleteStatement()
  508. {
  509. return string.Format("DELETE {0} FROM {1} {2}",
  510. (PrefixFieldWithTableName ? _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias) : string.Empty),
  511. _databaseType.EscapeTableName(modelDef.TableInfo.TableName) + (PrefixFieldWithTableName ? " " + _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias) : string.Empty),
  512. WhereExpression);
  513. }
  514. protected virtual string ToUpdateStatement(T item)
  515. {
  516. return ToUpdateStatement(item, false);
  517. }
  518. protected virtual string ToUpdateStatement(T item, bool excludeDefaults)
  519. {
  520. var setFields = new StringBuilder();
  521. foreach (var fieldDef in modelDef.Columns)
  522. {
  523. if (Context.UpdateFields.Count > 0 && !Context.UpdateFields.Contains(fieldDef.Value.MemberInfo.Name)) continue; // added
  524. object value = fieldDef.Value.GetValue(item);
  525. if (_database.Mapper != null)
  526. {
  527. var converter = _database.Mapper.GetToDbConverter(fieldDef.Value.ColumnType, fieldDef.Value.MemberInfo.GetMemberInfoType());
  528. if (converter != null)
  529. value = converter(value);
  530. }
  531. if (excludeDefaults && (value == null || value.Equals(MappingFactory.GetDefault(value.GetType())))) continue; //GetDefaultValue?
  532. if (setFields.Length > 0)
  533. setFields.Append(", ");
  534. setFields.AppendFormat("{0} = {1}", (PrefixFieldWithTableName ? _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias) + "." : string.Empty) + _databaseType.EscapeSqlIdentifier(fieldDef.Value.ColumnName), CreateParam(value));
  535. }
  536. if (PrefixFieldWithTableName)
  537. return string.Format("UPDATE {0} SET {2} FROM {1} {3}", _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias), _databaseType.EscapeTableName(modelDef.TableInfo.TableName) + " " + _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias), setFields, WhereExpression);
  538. else
  539. return string.Format("UPDATE {0} SET {1} {2}", _databaseType.EscapeTableName(modelDef.TableInfo.TableName), setFields, WhereExpression);
  540. }
  541. protected string ToWhereStatement()
  542. {
  543. return WhereExpression;
  544. }
  545. protected virtual string ToSelectStatement(bool applyPaging)
  546. {
  547. var sql = new StringBuilder();
  548. sql.Append(SelectExpression);
  549. sql.Append(string.IsNullOrEmpty(WhereExpression) ?
  550. "" :
  551. " \n" + WhereExpression);
  552. sql.Append(string.IsNullOrEmpty(GroupByExpression) ?
  553. "" :
  554. " \n" + GroupByExpression);
  555. sql.Append(string.IsNullOrEmpty(HavingExpression) ?
  556. "" :
  557. " \n" + HavingExpression);
  558. sql.Append(string.IsNullOrEmpty(OrderByExpression) ?
  559. "" :
  560. " \n" + OrderByExpression);
  561. return applyPaging ? ApplyPaging(sql.ToString(), ModelDef.QueryColumns.Select(x=>x.Value)) : sql.ToString();
  562. }
  563. //public virtual string ToCountStatement()
  564. //{
  565. // return OrmLiteConfig.DialectProvider.ToCountStatement(modelDef.ModelType, WhereExpression, null);
  566. //}
  567. private string SelectExpression
  568. {
  569. get
  570. {
  571. var selectMembersFromOrderBys = orderByMembers
  572. .Select(x => new SelectMember() { PocoColumn = x.PocoColumn, EntityType = x.EntityType})
  573. .Where(x => !selectMembers.Any(y => y.EntityType == x.EntityType && y.PocoColumn.MemberInfo.Name == x.PocoColumn.MemberInfo.Name));
  574. var morecols = selectMembers.Concat(selectMembersFromOrderBys);
  575. var cols = selectMembers.Count == 0 ? null : morecols.ToList();
  576. BuildSelectExpression(cols, false);
  577. return selectExpression;
  578. }
  579. set
  580. {
  581. selectExpression = value;
  582. }
  583. }
  584. private string WhereExpression
  585. {
  586. get
  587. {
  588. return whereExpression;
  589. }
  590. set
  591. {
  592. whereExpression = value;
  593. }
  594. }
  595. private string GroupByExpression
  596. {
  597. get
  598. {
  599. return groupBy;
  600. }
  601. set
  602. {
  603. groupBy = value;
  604. }
  605. }
  606. private string HavingExpression
  607. {
  608. get
  609. {
  610. return havingExpression;
  611. }
  612. set
  613. {
  614. havingExpression = value;
  615. }
  616. }
  617. private string OrderByExpression
  618. {
  619. get
  620. {
  621. return orderBy;
  622. }
  623. set
  624. {
  625. orderBy = value;
  626. }
  627. }
  628. protected virtual string LimitExpression
  629. {
  630. get
  631. {
  632. if (!Skip.HasValue) return "";
  633. string rows;
  634. if (Rows.HasValue)
  635. {
  636. rows = string.Format(",{0}", Rows.Value);
  637. }
  638. else
  639. {
  640. rows = string.Empty;
  641. }
  642. return string.Format("LIMIT {0}{1}", Skip.Value, rows);
  643. }
  644. }
  645. private int? Rows { get; set; }
  646. private int? Skip { get; set; }
  647. protected internal PocoData ModelDef
  648. {
  649. get
  650. {
  651. return modelDef;
  652. }
  653. set
  654. {
  655. modelDef = value;
  656. }
  657. }
  658. protected internal virtual object Visit(Expression exp)
  659. {
  660. if (exp == null) return string.Empty;
  661. switch (exp.NodeType)
  662. {
  663. case ExpressionType.Lambda:
  664. return VisitLambda(exp as LambdaExpression);
  665. case ExpressionType.MemberAccess:
  666. return VisitMemberAccess(exp as MemberExpression);
  667. case ExpressionType.Constant:
  668. return VisitConstant(exp as ConstantExpression);
  669. case ExpressionType.Add:
  670. case ExpressionType.AddChecked:
  671. case ExpressionType.Subtract:
  672. case ExpressionType.SubtractChecked:
  673. case ExpressionType.Multiply:
  674. case ExpressionType.MultiplyChecked:
  675. case ExpressionType.Divide:
  676. case ExpressionType.Modulo:
  677. case ExpressionType.And:
  678. case ExpressionType.AndAlso:
  679. case ExpressionType.Or:
  680. case ExpressionType.OrElse:
  681. case ExpressionType.LessThan:
  682. case ExpressionType.LessThanOrEqual:
  683. case ExpressionType.GreaterThan:
  684. case ExpressionType.GreaterThanOrEqual:
  685. case ExpressionType.Equal:
  686. case ExpressionType.NotEqual:
  687. case ExpressionType.Coalesce:
  688. case ExpressionType.ArrayIndex:
  689. case ExpressionType.RightShift:
  690. case ExpressionType.LeftShift:
  691. case ExpressionType.ExclusiveOr:
  692. //return "(" + VisitBinary(exp as BinaryExpression) + ")";
  693. return VisitBinary(exp as BinaryExpression);
  694. case ExpressionType.Conditional:
  695. return VisitConditional(exp as ConditionalExpression);
  696. case ExpressionType.Negate:
  697. case ExpressionType.NegateChecked:
  698. case ExpressionType.Not:
  699. case ExpressionType.Convert:
  700. case ExpressionType.ConvertChecked:
  701. case ExpressionType.ArrayLength:
  702. case ExpressionType.Quote:
  703. case ExpressionType.TypeAs:
  704. return VisitUnary(exp as UnaryExpression);
  705. case ExpressionType.Parameter:
  706. return VisitParameter(exp as ParameterExpression);
  707. case ExpressionType.Call:
  708. return VisitMethodCall(exp as MethodCallExpression);
  709. case ExpressionType.New:
  710. return VisitNew(exp as NewExpression);
  711. case ExpressionType.NewArrayInit:
  712. case ExpressionType.NewArrayBounds:
  713. return VisitNewArray(exp as NewArrayExpression);
  714. case ExpressionType.MemberInit:
  715. return this.VisitMemberInit((MemberInitExpression)exp);
  716. default:
  717. return exp.ToString();
  718. }
  719. }
  720. protected virtual Expression VisitMemberInit(MemberInitExpression init)
  721. {
  722. NewExpression n = init.NewExpression;
  723. IEnumerable<MemberBinding> bindings = this.VisitBindingList(init.Bindings);
  724. if (n != init.NewExpression || bindings != init.Bindings)
  725. {
  726. return Expression.MemberInit(n, bindings);
  727. }
  728. return init;
  729. }
  730. protected virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
  731. {
  732. for (int i = 0, n = original.Count; i < n; i++)
  733. {
  734. this.VisitBinding(original[i]);
  735. }
  736. return original;
  737. }
  738. protected virtual object VisitBinding(MemberBinding binding)
  739. {
  740. switch (binding.BindingType)
  741. {
  742. case MemberBindingType.Assignment:
  743. return this.VisitMemberAssignment((MemberAssignment)binding);
  744. case MemberBindingType.MemberBinding:
  745. return this.VisitMemberMemberBinding((MemberMemberBinding)binding);
  746. //case MemberBindingType.ListBinding:
  747. // return this.VisitMemberListBinding((MemberListBinding)binding);
  748. default:
  749. throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType));
  750. }
  751. }
  752. protected virtual object VisitMemberMemberBinding(MemberMemberBinding binding)
  753. {
  754. return VisitBindingList(binding.Bindings);
  755. }
  756. protected virtual object VisitMemberAssignment(MemberAssignment assignment)
  757. {
  758. return this.Visit(assignment.Expression);
  759. }
  760. protected virtual object VisitLambda(LambdaExpression lambda)
  761. {
  762. if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ")
  763. {
  764. MemberExpression m = lambda.Body as MemberExpression;
  765. if (m.Expression != null)
  766. {
  767. if (IsNullableMember(m))
  768. {
  769. string r = VisitMemberAccess(m.Expression as MemberExpression).ToString();
  770. return string.Format("{0} is not null", r);
  771. }
  772. else
  773. {
  774. string r = VisitMemberAccess(m).ToString();
  775. return string.Format("{0}={1}", r, GetQuotedTrueValue());
  776. }
  777. }
  778. }
  779. return Visit(lambda.Body);
  780. }
  781. private static bool IsNullableMember(MemberExpression m)
  782. {
  783. var member = m.Expression as MemberExpression;
  784. return member != null
  785. && (m.Member.Name == "HasValue")
  786. && member.Type.IsGenericType && member.Type.GetGenericTypeDefinition() == typeof(Nullable<>);
  787. }
  788. protected virtual object VisitBinary(BinaryExpression b)
  789. {
  790. object left, right;
  791. var operand = BindOperant(b.NodeType); //sep= " " ??
  792. if (operand == "AND" || operand == "OR")
  793. {
  794. var m = b.Left as MemberExpression;
  795. if (m != null && m.Expression != null
  796. && m.Expression.NodeType == ExpressionType.Parameter)
  797. left = new PartialSqlString(string.Format("{0} = {1}", VisitMemberAccess(m), GetQuotedTrueValue()));
  798. else
  799. left = Visit(b.Left);
  800. m = b.Right as MemberExpression;
  801. if (m != null && m.Expression != null
  802. && m.Expression.NodeType == ExpressionType.Parameter)
  803. right = new PartialSqlString(string.Format("{0} = {1}", VisitMemberAccess(m), GetQuotedTrueValue()));
  804. else
  805. right = Visit(b.Right);
  806. if (left as PartialSqlString == null && right as PartialSqlString == null)
  807. {
  808. var result = Expression.Lambda(b).Compile().DynamicInvoke();
  809. return new PartialSqlString(CreateParam(result));
  810. }
  811. if (left as PartialSqlString == null)
  812. left = ((bool)left) ? GetTrueExpression() : GetFalseExpression();
  813. if (right as PartialSqlString == null)
  814. right = ((bool)right) ? GetTrueExpression() : GetFalseExpression();
  815. }
  816. else
  817. {
  818. left = Visit(b.Left);
  819. right = Visit(b.Right);
  820. if (left as EnumMemberAccess != null && right as PartialSqlString == null)
  821. {
  822. var pc = ((EnumMemberAccess)left).PocoColumn;
  823. long numvericVal;
  824. if (pc.ColumnType == typeof(string))
  825. right = CreateParam(Enum.Parse(pc.MemberInfo.GetMemberInfoType(), right.ToString()).ToString());
  826. else if (Int64.TryParse(right.ToString(), out numvericVal))
  827. right = CreateParam(Enum.ToObject(pc.MemberInfo.GetMemberInfoType(), numvericVal));
  828. else
  829. right = CreateParam(right);
  830. }
  831. else if (left as NullableMemberAccess != null && right as PartialSqlString == null)
  832. {
  833. operand = ((bool)right) ? "is not" : "is";
  834. right = new PartialSqlString("null");
  835. }
  836. else if (right as EnumMemberAccess != null && left as PartialSqlString == null)
  837. {
  838. var pc = ((EnumMemberAccess)right).PocoColumn;
  839. //enum value was returned by Visit(b.Left)
  840. long numvericVal;
  841. if (pc.ColumnType == typeof(string))
  842. left = CreateParam(Enum.Parse(pc.MemberInfo.GetMemberInfoType(), left.ToString()).ToString());
  843. else if (Int64.TryParse(left.ToString(), out numvericVal))
  844. left = CreateParam(Enum.ToObject(pc.MemberInfo.GetMemberInfoType(), numvericVal));
  845. else
  846. left = CreateParam(left);
  847. }
  848. else if (left as PartialSqlString == null && right as PartialSqlString == null)
  849. {
  850. var result = Expression.Lambda(b).Compile().DynamicInvoke();
  851. return result;
  852. }
  853. else if (left as PartialSqlString == null)
  854. left = CreateParam(left);
  855. else if (right as PartialSqlString == null)
  856. right = CreateParam(right);
  857. }
  858. if (operand == "=" && right.ToString().Equals("null", StringComparison.InvariantCultureIgnoreCase)) operand = "is";
  859. else if (operand == "<>" && right.ToString().Equals("null", StringComparison.InvariantCultureIgnoreCase)) operand = "is not";
  860. switch (operand)
  861. {
  862. case "MOD":
  863. case "COALESCE":
  864. return new PartialSqlString(string.Format("{0}({1},{2})", operand, left, right));
  865. default:
  866. return new PartialSqlString("(" + left + sep + operand + sep + right + ")");
  867. }
  868. }
  869. protected virtual object VisitMemberAccess(MemberExpression m)
  870. {
  871. bool isNull = false;
  872. if (IsNullableMember(m))
  873. {
  874. m = m.Expression as MemberExpression;
  875. isNull = true;
  876. }
  877. if (m.Expression != null
  878. && (m.Expression.NodeType == ExpressionType.Parameter
  879. || m.Expression.NodeType == ExpressionType.Convert
  880. || m.Expression.NodeType == ExpressionType.MemberAccess))
  881. {
  882. var propertyInfo = (PropertyInfo)m.Member;
  883. var type = GetCorrectType(m);
  884. var localModelDef = _database.PocoDataFactory.ForType(type);
  885. var pocoColumn = localModelDef.Columns.Values.Single(x => x.MemberInfo.Name == m.Member.Name);
  886. var columnName = (PrefixFieldWithTableName ? _databaseType.EscapeTableName(localModelDef.TableInfo.AutoAlias) + "." : "")
  887. + _databaseType.EscapeSqlIdentifier(pocoColumn.ColumnName);
  888. generalMembers.Add(new GeneralMember() { EntityType = type, PocoColumn = pocoColumn });
  889. if (isNull)
  890. return new NullableMemberAccess(pocoColumn, columnName, type);
  891. if (propertyInfo.PropertyType.IsEnum)
  892. return new EnumMemberAccess(pocoColumn, columnName, type);
  893. return new MemberAccessString(pocoColumn, columnName, type);
  894. }
  895. var memberExp = Expression.Convert(m, typeof(object));
  896. var lambda = Expression.Lambda<Func<object>>(memberExp);
  897. var getter = lambda.Compile();
  898. return getter();
  899. }
  900. private Type GetCorrectType(MemberExpression m)
  901. {
  902. var type = m.Member.DeclaringType;
  903. if (m.Expression.NodeType == ExpressionType.MemberAccess)
  904. {
  905. type = ((PropertyInfo)((MemberExpression)m.Expression).Member).PropertyType;
  906. }
  907. else if (m.Expression.NodeType == ExpressionType.Parameter)
  908. {
  909. type = m.Expression.Type;
  910. }
  911. return type;
  912. }
  913. protected virtual object VisitNew(NewExpression nex)
  914. {
  915. var member = Expression.Convert(nex, typeof(object));
  916. var lambda = Expression.Lambda<Func<object>>(member);
  917. try
  918. {
  919. var getter = lambda.Compile();
  920. return getter();
  921. }
  922. catch (System.InvalidOperationException)
  923. {
  924. List<MemberAccessString> exprs = VisitExpressionList(nex.Arguments).OfType<MemberAccessString>().ToList();
  925. StringBuilder r = new StringBuilder();
  926. for (int i = 0; i < exprs.Count; i++)
  927. {
  928. if (_projection)
  929. {
  930. selectMembers.Add(new SelectMember()
  931. {
  932. EntityType = exprs[i].Type,
  933. PocoColumn = exprs[i].PocoColumn,
  934. SelectSql = exprs[i].Text
  935. });
  936. continue;
  937. }
  938. r.AppendFormat("{0}{1}", r.Length > 0 ? "," : "", exprs[i]);
  939. if (nex.Members[i] != null)
  940. {
  941. var col = modelDef.Columns.SingleOrDefault(x => x.Value.MemberInfo.Name == nex.Members[i].Name);
  942. if (col.Value != null)
  943. {
  944. var sel = new SelectMember()
  945. {
  946. EntityType = modelDef.type,
  947. SelectSql = exprs[i].ToString(),
  948. PocoColumn = col.Value
  949. };
  950. var memberName = _databaseType.EscapeSqlIdentifier(col.Value.ColumnName);
  951. if (memberName != exprs[i].ToString())
  952. {
  953. var al = string.Format(" AS {0}", _databaseType.EscapeSqlIdentifier(col.Value.AutoAlias));
  954. r.AppendFormat(al);
  955. sel.SelectSql += al;
  956. }
  957. selectMembers.Add(sel);
  958. }
  959. }
  960. }
  961. return r.ToString();
  962. }
  963. }
  964. protected virtual object VisitParameter(ParameterExpression p)
  965. {
  966. return p.Name;
  967. }
  968. List<object> _params = new List<object>();
  969. int _paramCounter = 0;
  970. string paramPrefix;
  971. private bool _projection;
  972. public SqlExpressionContext Context { get; private set; }
  973. protected virtual object VisitConstant(ConstantExpression c)
  974. {
  975. if (c.Value == null)
  976. return new PartialSqlString("null");
  977. return c.Value;
  978. }
  979. protected virtual object VisitConditional(ConditionalExpression conditional)
  980. {
  981. sep = " ";
  982. var test = Visit(conditional.Test);
  983. var trueSql = Visit(conditional.IfTrue);
  984. var falseSql = Visit(conditional.IfFalse);
  985. return new PartialSqlString(string.Format("(case when {0} then {1} else {2} end)", test, trueSql, falseSql));
  986. }
  987. private string CreateParam(object value)
  988. {
  989. string paramPlaceholder = paramPrefix + _paramCounter++;
  990. _params.Add(value);
  991. return paramPlaceholder;
  992. }
  993. protected virtual object VisitUnary(UnaryExpression u)
  994. {
  995. switch (u.NodeType)
  996. {
  997. case ExpressionType.Not:
  998. var o = Visit(u.Operand);
  999. if (o as PartialSqlString == null)
  1000. return !((bool)o);
  1001. if (o as MemberAccessString != null)
  1002. {
  1003. if (o as NullableMemberAccess != null)
  1004. o = o + " is not null";
  1005. else
  1006. o = o + " = " + GetQuotedTrueValue();
  1007. }
  1008. return new PartialSqlString("NOT (" + o + ")");
  1009. case ExpressionType.Convert:
  1010. if (u.Method != null)
  1011. return Expression.Lambda(u).Compile().DynamicInvoke();
  1012. break;
  1013. }
  1014. return Visit(u.Operand);
  1015. }
  1016. private bool IsColumnAccess(MethodCallExpression m)
  1017. {
  1018. if (m.Object != null && m.Object as MethodCallExpression != null)
  1019. return IsColumnAccess(m.Object as MethodCallExpression);
  1020. var exp = m.Object as MemberExpression;
  1021. return exp != null
  1022. && exp.Expression != null
  1023. && ((exp.Expression.Type == typeof(T) && exp.Expression.NodeType == ExpressionType.Parameter
  1024. || exp.Expression.NodeType == ExpressionType.MemberAccess));
  1025. }
  1026. protected virtual object VisitMethodCall(MethodCallExpression m)
  1027. {
  1028. if (m.Method.DeclaringType == typeof(S))
  1029. return VisitSqlMethodCall(m);
  1030. if (IsStaticArrayMethod(m))
  1031. return VisitStaticArrayMethodCall(m);
  1032. if (IsEnumerableMethod(m))
  1033. return VisitEnumerableMethodCall(m);
  1034. if (IsColumnAccess(m))
  1035. return VisitColumnAccessMethod(m);
  1036. return Expression.Lambda(m).Compile().DynamicInvoke();
  1037. }
  1038. private bool IsStaticArrayMethod(MethodCallExpression m)
  1039. {
  1040. if (m.Object == null && m.Method.Name == "Contains")
  1041. {
  1042. return m.Arguments.Count == 2;
  1043. }
  1044. return false;
  1045. }
  1046. private bool IsEnumerableMethod(MethodCallExpression m)
  1047. {
  1048. if (m.Object != null
  1049. && m.Object.Type.IsOrHasGenericInterfaceTypeOf(typeof(IEnumerable<>))
  1050. && m.Object.Type != typeof(string)
  1051. && m.Method.Name == "Contains")
  1052. {
  1053. return m.Arguments.Count == 1;
  1054. }
  1055. return false;
  1056. }
  1057. protected virtual object VisitEnumerableMethodCall(MethodCallExpression m)
  1058. {
  1059. switch (m.Method.Name)
  1060. {
  1061. case "Contains":
  1062. List<Object> args = this.VisitExpressionList(m.Arguments);
  1063. object quotedColName = args[0];
  1064. return BuildInStatement(m.Object, quotedColName);
  1065. default:
  1066. throw new NotSupportedException();
  1067. }
  1068. }
  1069. protected virtual object VisitStaticArrayMethodCall(MethodCallExpression m)
  1070. {
  1071. switch (m.Method.Name)
  1072. {
  1073. case "Contains":
  1074. List<Object> args = this.VisitExpressionList(m.Arguments);
  1075. object quotedColName = args[1];
  1076. Expression memberExpr = m.Arguments[0];
  1077. if (memberExpr.NodeType == ExpressionType.MemberAccess)
  1078. memberExpr = (m.Arguments[0] as MemberExpression);
  1079. return BuildInStatement(memberExpr, quotedColName);
  1080. default:
  1081. throw new NotSupportedException();
  1082. }
  1083. }
  1084. private StringBuilder FlattenList(IEnumerable inArgs)
  1085. {
  1086. StringBuilder sIn = new StringBuilder();
  1087. foreach (Object e in inArgs)
  1088. {
  1089. if (!typeof(ICollection).IsAssignableFrom(e.GetType()))
  1090. {
  1091. sIn.AppendFormat("{0}{1}", sIn.Length > 0 ? "," : "", CreateParam(e));
  1092. }
  1093. else
  1094. {
  1095. var listArgs = e as ICollection;
  1096. foreach (Object el in listArgs)
  1097. {
  1098. sIn.AppendFormat("{0}{1}", sIn.Length > 0 ? "," : "", CreateParam(el));
  1099. }
  1100. }
  1101. }
  1102. if (sIn.Length == 0)
  1103. {
  1104. sIn.AppendFormat("select 1 /*poco_dual*/ where 1 = 0");
  1105. }
  1106. return sIn;
  1107. }
  1108. protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
  1109. {
  1110. List<Object> list = new List<Object>();
  1111. for (int i = 0, n = original.Count; i < n; i++)
  1112. {
  1113. if (original[i].NodeType == ExpressionType.NewArrayInit ||
  1114. original[i].NodeType == ExpressionType.NewArrayBounds)
  1115. {
  1116. list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression));
  1117. }
  1118. else
  1119. list.Add(Visit(original[i]));
  1120. }
  1121. return list;
  1122. }
  1123. protected virtual List<Object> VisitConstantList(ReadOnlyCollection<Expression> original)
  1124. {
  1125. List<Object> list = new List<Object>();
  1126. for (int i = 0, n = original.Count; i < n; i++)
  1127. {
  1128. list.Add(original[i].GetConstantValue<object>());
  1129. }
  1130. return list;
  1131. }
  1132. protected virtual object VisitNewArray(NewArrayExpression na)
  1133. {
  1134. List<Object> exprs = VisitExpressionList(na.Expressions);
  1135. StringBuilder r = new StringBuilder();
  1136. foreach (Object e in exprs)
  1137. {
  1138. r.Append(r.Length > 0 ? "," + e : e);
  1139. }
  1140. return r.ToString();
  1141. }
  1142. protected virtual List<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
  1143. {
  1144. List<Object> exprs = VisitExpressionList(na.Expressions);
  1145. return exprs;
  1146. }
  1147. protected virtual string BindOperant(ExpressionType e)
  1148. {
  1149. switch (e)
  1150. {
  1151. case ExpressionType.Equal:
  1152. return "=";
  1153. case ExpressionType.NotEqual:
  1154. return "<>";
  1155. case ExpressionType.GreaterThan:
  1156. return ">";
  1157. case ExpressionType.GreaterThanOrEqual:
  1158. return ">=";
  1159. case ExpressionType.LessThan:
  1160. return "<";
  1161. case ExpressionType.LessThanOrEqual:
  1162. return "<=";
  1163. case ExpressionType.AndAlso:
  1164. return "AND";
  1165. case ExpressionType.OrElse:
  1166. return "OR";
  1167. case ExpressionType.Add:
  1168. return "+";
  1169. case ExpressionType.Subtract:
  1170. return "-";
  1171. case ExpressionType.Multiply:
  1172. return "*";
  1173. case ExpressionType.Divide:
  1174. return "/";
  1175. case ExpressionType.Modulo:
  1176. return "MOD";
  1177. case ExpressionType.Coalesce:
  1178. return "COALESCE";
  1179. default:
  1180. return e.ToString();
  1181. }
  1182. }
  1183. protected virtual string GetQuotedColumnName(string memberName)
  1184. {
  1185. var fd = modelDef.Columns.Values.FirstOrDefault(x => x.MemberInfo.Name == memberName);
  1186. string fn = fd != null ? fd.ColumnName : memberName;
  1187. return _databaseType.EscapeSqlIdentifier(fn);
  1188. }
  1189. protected string RemoveQuoteFromAlias(string exp)
  1190. {
  1191. if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
  1192. && (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
  1193. {
  1194. exp = exp.Remove(0, 1);
  1195. exp = exp.Remove(exp.Length - 1, 1);
  1196. }
  1197. return exp;
  1198. }
  1199. protected object GetTrueExpression()
  1200. {
  1201. return new PartialSqlString(string.Format("({0}={1})", GetQuotedTrueValue(), GetQuotedTrueValue()));
  1202. }
  1203. protected object GetFalseExpression()
  1204. {
  1205. return new PartialSqlString(string.Format("({0}={1})", GetQuotedTrueValue(), GetQuotedFalseValue()));
  1206. }
  1207. protected object GetQuotedTrueValue()
  1208. {
  1209. return CreateParam(true);
  1210. }
  1211. protected object GetQuotedFalseValue()
  1212. {
  1213. return CreateParam(false);
  1214. }
  1215. private void BuildSelectExpression(List<SelectMember> fields, bool distinct)
  1216. {
  1217. var cols = fields ?? modelDef.QueryColumns.Select(x => new SelectMember{ PocoColumn = x.Value, EntityType = modelDef.type });
  1218. selectExpression = string.Format("SELECT {0}{1} \nFROM {2}",
  1219. (distinct ? "DISTINCT " : ""),
  1220. string.Join(", ", cols.Select(x =>
  1221. {
  1222. if (x.SelectSql == null)
  1223. return (PrefixFieldWithTableName
  1224. ? _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias) + "." + _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName) + " as " + _databaseType.EscapeSqlIdentifier(x.PocoColumn.AutoAlias)
  1225. : _databaseType.EscapeSqlIdentifier(x.PocoColumn.ColumnName));
  1226. return x.SelectSql;
  1227. }).ToArray()),
  1228. _databaseType.EscapeTableName(modelDef.TableInfo.TableName) + (PrefixFieldWithTableName ? " " + _databaseType.EscapeTableName(modelDef.TableInfo.AutoAlias) : string.Empty));
  1229. }
  1230. internal List<PocoColumn> GetAllMembers()
  1231. {
  1232. return modelDef.Columns.Values.ToList();
  1233. }
  1234. protected virtual string ApplyPaging(string sql, IEnumerable<PocoColumn> columns)
  1235. {
  1236. if (!Rows.HasValue || Rows == 0)
  1237. return sql;
  1238. string sqlPage;
  1239. var parms = _params.Select(x => x).ToArray();
  1240. // Split the SQL
  1241. PagingHelper.SQLParts parts;
  1242. if (!PagingHelper.SplitSQL(sql, out parts)) throw new Exception("Unable to parse SQL statement for paged query");
  1243. if (columns != null && columns.Any() && _databaseType.UseColumnAliases())
  1244. parts.sqlColumns = string.Join(", ", columns.Select(x => _databaseType.EscapeSqlIdentifier(x.AutoAlias)).ToArray());
  1245. sqlPage = _databaseType.BuildPageQuery(Skip ?? 0, Rows ?? 0, parts, ref parms);
  1246. _params.Clear();
  1247. _params.AddRange(parms);
  1248. return sqlPage;
  1249. }
  1250. private string BuildInStatement(Expression m, object quotedColName)
  1251. {
  1252. string statement;
  1253. var member = Expression.Convert(m, typeof(object));
  1254. var lambda = Expression.Lambda<Func<object>>(member);
  1255. var getter = lambda.Compile();
  1256. if (quotedColName == null)
  1257. quotedColName = Visit(m);
  1258. var inArgs = getter() as IEnumerable;
  1259. var sIn = FlattenList(inArgs);
  1260. statement = string.Format("{0} {1} ({2})", quotedColName, "IN", sIn);
  1261. return statement;
  1262. }
  1263. protected virtual object VisitSqlMethodCall(MethodCallExpression m)
  1264. {
  1265. List<Object> args = this.VisitExpressionList(m.Arguments);
  1266. object quotedColName = args[0];
  1267. args.RemoveAt(0);
  1268. string statement;
  1269. switch (m.Method.Name)
  1270. {
  1271. case "In":
  1272. statement = BuildInStatement(m.Arguments[1], quotedColName);
  1273. break;
  1274. case "Desc":
  1275. statement = string.Format("{0} DESC", quotedColName);
  1276. break;
  1277. case "As":
  1278. statement = string.Format("{0} As {1}", quotedColName,
  1279. _databaseType.EscapeSqlIdentifier(RemoveQuoteFromAlias(args[0].ToString())));
  1280. break;
  1281. case "Sum":
  1282. case "Count":
  1283. case "Min":
  1284. case "Max":
  1285. case "Avg":
  1286. statement = string.Format("{0}({1}{2})",
  1287. m.Method.Name.ToUpper(),
  1288. quotedColName,
  1289. args.Count == 1 ? string.Format(",{0}", args[0]) : "");
  1290. break;
  1291. default:
  1292. throw new NotSupportedException();
  1293. }
  1294. return new PartialSqlString(statement);
  1295. }
  1296. protected virtual object VisitColumnAccessMethod(MethodCallExpression m)
  1297. {
  1298. if (_projection)
  1299. return null;
  1300. List<Object> args = this.VisitExpressionList(m.Arguments);
  1301. var quotedColName = (MemberAccessString)Visit(m.Object);
  1302. quotedColName.Text = _projection ? _databaseType.EscapeSqlIdentifier(quotedColName.PocoColumn.AutoAlias) : quotedColName.Text;
  1303. string statement;
  1304. switch (m.Method.Name)
  1305. {
  1306. case "ToUpper":
  1307. statement = string.Format("upper({0})", quotedColName);
  1308. break;
  1309. case "ToLower":
  1310. statement = string.Format("lower({0})", quotedColName);
  1311. break;
  1312. case "StartsWith":
  1313. statement = string.Format("upper({0}) like {1}", quotedColName, CreateParam(args[0].ToString().ToUpper() + "%"));
  1314. break;
  1315. case "EndsWith":
  1316. statement = string.Format("upper({0}) like {1}", quotedColName, CreateParam("%" + args[0].ToString().ToUpper()));
  1317. break;
  1318. case "Contains":
  1319. statement = string.Format("upper({0}) like {1}", quotedColName, CreateParam("%" + args[0].ToString().ToUpper() + "%"));
  1320. break;
  1321. case "Substring":
  1322. var startIndex = Int32.Parse(args[0].ToString()) + 1;
  1323. if (args.Count == 2)
  1324. {
  1325. var length = Int32.Parse(args[1].ToString());
  1326. statement = string.Format("substring({0},{1},{2})", quotedColName, startIndex, length);
  1327. }
  1328. else
  1329. statement = string.Format("substring({0},{1},8000)", quotedColName, startIndex);
  1330. break;
  1331. case "Equals":
  1332. statement = string.Format("({0} = {1})", quotedColName, args[0]);
  1333. break;
  1334. case "ToString":
  1335. statement = string.Empty;
  1336. break;
  1337. default:
  1338. throw new NotSupportedException();
  1339. }
  1340. quotedColName.Text = statement;
  1341. return quotedColName;
  1342. }
  1343. }
  1344. public class PartialSqlString
  1345. {
  1346. public PartialSqlString(string text)
  1347. {
  1348. Text = text;
  1349. }
  1350. public string Text { get; set; }
  1351. public override string ToString()
  1352. {
  1353. return Text;
  1354. }
  1355. }
  1356. public class MemberAccessString : PartialSqlString
  1357. {
  1358. public MemberAccessString(PocoColumn pocoColumn, string text, Type type)
  1359. : base(text)
  1360. {
  1361. PocoColumn = pocoColumn;
  1362. Type = type;
  1363. }
  1364. public PocoColumn PocoColumn { get; private set; }
  1365. public Type Type { get; set; }
  1366. }
  1367. public class NullableMemberAccess : MemberAccessString
  1368. {
  1369. public NullableMemberAccess(PocoColumn pocoColumn, string text, Type type) : base(pocoColumn, text, type)
  1370. {
  1371. }
  1372. }
  1373. public class EnumMemberAccess : MemberAccessString
  1374. {
  1375. public EnumMemberAccess(PocoColumn pocoColumn, string text, Type type) : base(pocoColumn, text, type)
  1376. {
  1377. }
  1378. }
  1379. public static class LinqExtensions
  1380. {
  1381. /// <summary>
  1382. /// Gets the constant value.
  1383. /// </summary>
  1384. /// <param retval="exp">The exp.</param>
  1385. /// <returns>The get constant value.</returns>
  1386. public static T GetConstantValue<T>(this Expression exp)
  1387. {
  1388. T result = default(T);
  1389. if (exp is ConstantExpression)
  1390. {
  1391. var c = (ConstantExpression)exp;
  1392. result = (T)c.Value;
  1393. }
  1394. return result;
  1395. }
  1396. }
  1397. }