Mercurial > pub > bltoolkit
view Source/Data/Sql/QueryVisitor.cs @ 6:11b6da379593
Исправлена странная ошибка при использовании OfType<...>().Where(...)
author | cin |
---|---|
date | Mon, 05 Dec 2016 05:50:52 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections.Generic; namespace BLToolkit.Data.Sql { using VisitFunc = Func<IQueryElement,bool>; using FindFunc = Func<IQueryElement,bool>; using ConvertFunc = Func<IQueryElement,IQueryElement>; public class QueryVisitor { #region Visit readonly Dictionary<IQueryElement,IQueryElement> _visitedElements = new Dictionary<IQueryElement, IQueryElement>(); public Dictionary<IQueryElement,IQueryElement> VisitedElements { get { return _visitedElements; } } bool _all; Func<IQueryElement,bool> _action1; Action<IQueryElement> _action2; public void VisitParentFirst(IQueryElement element, Func<IQueryElement,bool> action) { _visitedElements.Clear(); _action1 = action; Visit1(element); } void Visit1(IQueryElement element) { if (element == null || _visitedElements.ContainsKey(element)) return; _visitedElements.Add(element, element); if (!_action1(element)) return; switch (element.ElementType) { case QueryElementType.SqlFunction: { foreach (var p in ((SqlFunction)element).Parameters) Visit1(p); break; } case QueryElementType.SqlExpression: { foreach (var v in ((SqlExpression)element).Parameters) Visit1(v); break; } case QueryElementType.SqlBinaryExpression: { //var bexpr = (SqlBinaryExpression)element; Visit1(((SqlBinaryExpression)element).Expr1); Visit1(((SqlBinaryExpression)element).Expr2); break; } case QueryElementType.SqlTable: { var table = (SqlTable)element; Visit1(table.All); foreach (var field in table.Fields.Values) Visit1(field); foreach (var join in table.Joins) Visit1(join); if (table.TableArguments != null) foreach (var a in table.TableArguments) Visit1(a); break; } case QueryElementType.Join: { foreach (var j in ((Join)element).JoinOns) Visit1(j); break; } case QueryElementType.Column: { Visit1(((SqlQuery.Column)element).Expression); break; } case QueryElementType.TableSource: { //var table = ((SqlQuery.TableSource)element); Visit1(((SqlQuery.TableSource)element).Source); foreach (var j in ((SqlQuery.TableSource)element).Joins) Visit1(j); break; } case QueryElementType.JoinedTable: { //var join = (SqlQuery.JoinedTable)element; Visit1(((SqlQuery.JoinedTable)element).Table); Visit1(((SqlQuery.JoinedTable)element).Condition); break; } case QueryElementType.SearchCondition: { foreach (var c in ((SqlQuery.SearchCondition)element).Conditions) Visit1(c); break; } case QueryElementType.Condition: { Visit1(((SqlQuery.Condition)element).Predicate); break; } case QueryElementType.ExprPredicate: { Visit1(((SqlQuery.Predicate.Expr)element).Expr1); break; } case QueryElementType.NotExprPredicate: { Visit1(((SqlQuery.Predicate.NotExpr)element).Expr1); break; } case QueryElementType.ExprExprPredicate: { //var p = ((SqlQuery.Predicate.ExprExpr)element); Visit1(((SqlQuery.Predicate.ExprExpr)element).Expr1); Visit1(((SqlQuery.Predicate.ExprExpr)element).Expr2); break; } case QueryElementType.LikePredicate: { //var p = ((SqlQuery.Predicate.Like)element); Visit1(((SqlQuery.Predicate.Like)element).Expr1); Visit1(((SqlQuery.Predicate.Like)element).Expr2); Visit1(((SqlQuery.Predicate.Like)element).Escape); break; } case QueryElementType.BetweenPredicate: { //var p = (SqlQuery.Predicate.Between)element; Visit1(((SqlQuery.Predicate.Between)element).Expr1); Visit1(((SqlQuery.Predicate.Between)element).Expr2); Visit1(((SqlQuery.Predicate.Between)element).Expr3); break; } case QueryElementType.IsNullPredicate: { Visit1(((SqlQuery.Predicate.IsNull)element).Expr1); break; } case QueryElementType.InSubQueryPredicate: { //var p = (SqlQuery.Predicate.InSubQuery)element; Visit1(((SqlQuery.Predicate.InSubQuery)element).Expr1); Visit1(((SqlQuery.Predicate.InSubQuery)element).SubQuery); break; } case QueryElementType.InListPredicate: { //var p = (SqlQuery.Predicate.InList)element; Visit1(((SqlQuery.Predicate.InList)element).Expr1); foreach (var value in ((SqlQuery.Predicate.InList)element).Values) Visit1(value); break; } case QueryElementType.FuncLikePredicate: { Visit1(((SqlQuery.Predicate.FuncLike)element).Function); break; } case QueryElementType.SetExpression: { //var s = (SqlQuery.SetExpression)element; Visit1(((SqlQuery.SetExpression)element).Column); Visit1(((SqlQuery.SetExpression)element).Expression); break; } case QueryElementType.InsertClause: { //var sc = (SqlQuery.InsertClause)element; if (((SqlQuery.InsertClause)element).Into != null) Visit1(((SqlQuery.InsertClause)element).Into); foreach (var c in ((SqlQuery.InsertClause)element).Items.ToArray()) Visit1(c); break; } case QueryElementType.UpdateClause: { //var sc = (SqlQuery.UpdateClause)element; if (((SqlQuery.UpdateClause)element).Table != null) Visit1(((SqlQuery.UpdateClause)element).Table); foreach (var c in ((SqlQuery.UpdateClause)element).Items.ToArray()) Visit1(c); foreach (var c in ((SqlQuery.UpdateClause)element).Keys. ToArray()) Visit1(c); break; } case QueryElementType.DeleteClause: { if (((SqlQuery.DeleteClause)element).Table != null) Visit1(((SqlQuery.DeleteClause)element).Table); break; } case QueryElementType.SelectClause: { //var sc = (SqlQuery.SelectClause)element; Visit1(((SqlQuery.SelectClause)element).TakeValue); Visit1(((SqlQuery.SelectClause)element).SkipValue); foreach (var c in ((SqlQuery.SelectClause)element).Columns.ToArray()) Visit1(c); break; } case QueryElementType.FromClause: { foreach (var t in ((SqlQuery.FromClause)element).Tables) Visit1(t); break; } case QueryElementType.WhereClause: { Visit1(((SqlQuery.WhereClause)element).SearchCondition); break; } case QueryElementType.GroupByClause: { foreach (var i in ((SqlQuery.GroupByClause)element).Items) Visit1(i); break; } case QueryElementType.OrderByClause: { foreach (var i in ((SqlQuery.OrderByClause)element).Items) Visit1(i); break; } case QueryElementType.OrderByItem: { Visit1(((SqlQuery.OrderByItem)element).Expression); break; } case QueryElementType.Union: Visit1(((SqlQuery.Union)element).SqlQuery); break; case QueryElementType.SqlQuery: { if (_all) { if (_visitedElements.ContainsKey(element)) return; _visitedElements.Add(element, element); } var q = (SqlQuery)element; switch (q.QueryType) { case QueryType.InsertOrUpdate : Visit1(q.Insert); Visit1(q.Update); if (q.From.Tables.Count == 0) break; goto default; case QueryType.Update : Visit1(q.Update); break; case QueryType.Delete : Visit1(q.Delete); Visit1(q.Select); break; case QueryType.Insert : Visit1(q.Insert); if (q.From.Tables.Count != 0) Visit1(q.Select); break; default : Visit1(q.Select); break; } Visit1(q.From); Visit1(q.Where); Visit1(q.GroupBy); Visit1(q.Having); Visit1(q.OrderBy); if (q.HasUnion) { foreach (var i in q.Unions) { if (i.SqlQuery == q) throw new InvalidOperationException(); Visit1(i); } } break; } } } public void Visit(IQueryElement element, Action<IQueryElement> action) { _visitedElements.Clear(); _all = false; _action2 = action; Visit2(element); } public void VisitAll(IQueryElement element, Action<IQueryElement> action) { _visitedElements.Clear(); _all = true; _action2 = action; Visit2(element); } void Visit2(IQueryElement element) { if (element == null || !_all && _visitedElements.ContainsKey(element)) return; switch (element.ElementType) { case QueryElementType.SqlFunction: { foreach (var p in ((SqlFunction)element).Parameters) Visit2(p); break; } case QueryElementType.SqlExpression: { foreach (var v in ((SqlExpression)element).Parameters) Visit2(v); break; } case QueryElementType.SqlBinaryExpression: { //var bexpr = (SqlBinaryExpression)element; Visit2(((SqlBinaryExpression)element).Expr1); Visit2(((SqlBinaryExpression)element).Expr2); break; } case QueryElementType.SqlTable: { var table = (SqlTable)element; Visit2(table.All); foreach (var field in table.Fields.Values) Visit2(field); foreach (var join in table.Joins) Visit2(join); if (table.TableArguments != null) foreach (var a in table.TableArguments) Visit2(a); break; } case QueryElementType.Join: { foreach (var j in ((Join)element).JoinOns) Visit2(j); break; } case QueryElementType.Column: { Visit2(((SqlQuery.Column)element).Expression); break; } case QueryElementType.TableSource: { //var table = ((SqlQuery.TableSource)element); Visit2(((SqlQuery.TableSource)element).Source); foreach (var j in ((SqlQuery.TableSource)element).Joins) Visit2(j); break; } case QueryElementType.JoinedTable: { //var join = (SqlQuery.JoinedTable)element; Visit2(((SqlQuery.JoinedTable)element).Table); Visit2(((SqlQuery.JoinedTable)element).Condition); break; } case QueryElementType.SearchCondition: { foreach (var c in ((SqlQuery.SearchCondition)element).Conditions) Visit2(c); break; } case QueryElementType.Condition: { Visit2(((SqlQuery.Condition)element).Predicate); break; } case QueryElementType.ExprPredicate: { Visit2(((SqlQuery.Predicate.Expr)element).Expr1); break; } case QueryElementType.NotExprPredicate: { Visit2(((SqlQuery.Predicate.NotExpr)element).Expr1); break; } case QueryElementType.ExprExprPredicate: { //var p = ((SqlQuery.Predicate.ExprExpr)element); Visit2(((SqlQuery.Predicate.ExprExpr)element).Expr1); Visit2(((SqlQuery.Predicate.ExprExpr)element).Expr2); break; } case QueryElementType.LikePredicate: { //var p = ((SqlQuery.Predicate.Like)element); Visit2(((SqlQuery.Predicate.Like)element).Expr1); Visit2(((SqlQuery.Predicate.Like)element).Expr2); Visit2(((SqlQuery.Predicate.Like)element).Escape); break; } case QueryElementType.BetweenPredicate: { //var p = (SqlQuery.Predicate.Between)element; Visit2(((SqlQuery.Predicate.Between)element).Expr1); Visit2(((SqlQuery.Predicate.Between)element).Expr2); Visit2(((SqlQuery.Predicate.Between)element).Expr3); break; } case QueryElementType.IsNullPredicate: { Visit2(((SqlQuery.Predicate.IsNull)element).Expr1); break; } case QueryElementType.InSubQueryPredicate: { //var p = (SqlQuery.Predicate.InSubQuery)element; Visit2(((SqlQuery.Predicate.InSubQuery)element).Expr1); Visit2(((SqlQuery.Predicate.InSubQuery)element).SubQuery); break; } case QueryElementType.InListPredicate: { //var p = (SqlQuery.Predicate.InList)element; Visit2(((SqlQuery.Predicate.InList)element).Expr1); foreach (var value in ((SqlQuery.Predicate.InList)element).Values) Visit2(value); break; } case QueryElementType.FuncLikePredicate: { Visit2(((SqlQuery.Predicate.FuncLike)element).Function); break; } case QueryElementType.SetExpression: { //var s = (SqlQuery.SetExpression)element; Visit2(((SqlQuery.SetExpression)element).Column); Visit2(((SqlQuery.SetExpression)element).Expression); break; } case QueryElementType.InsertClause: { //var sc = (SqlQuery.InsertClause)element; if (((SqlQuery.InsertClause)element).Into != null) Visit2(((SqlQuery.InsertClause)element).Into); foreach (var c in ((SqlQuery.InsertClause)element).Items.ToArray()) Visit2(c); break; } case QueryElementType.UpdateClause: { //var sc = (SqlQuery.UpdateClause)element; if (((SqlQuery.UpdateClause)element).Table != null) Visit2(((SqlQuery.UpdateClause)element).Table); foreach (var c in ((SqlQuery.UpdateClause)element).Items.ToArray()) Visit2(c); foreach (var c in ((SqlQuery.UpdateClause)element).Keys. ToArray()) Visit2(c); break; } case QueryElementType.DeleteClause: { if (((SqlQuery.DeleteClause)element).Table != null) Visit2(((SqlQuery.DeleteClause)element).Table); break; } case QueryElementType.SelectClause: { //var sc = (SqlQuery.SelectClause)element; Visit2(((SqlQuery.SelectClause)element).TakeValue); Visit2(((SqlQuery.SelectClause)element).SkipValue); foreach (var c in ((SqlQuery.SelectClause)element).Columns.ToArray()) Visit2(c); break; } case QueryElementType.FromClause: { foreach (var t in ((SqlQuery.FromClause)element).Tables) Visit2(t); break; } case QueryElementType.WhereClause: { Visit2(((SqlQuery.WhereClause)element).SearchCondition); break; } case QueryElementType.GroupByClause: { foreach (var i in ((SqlQuery.GroupByClause)element).Items) Visit2(i); break; } case QueryElementType.OrderByClause: { foreach (var i in ((SqlQuery.OrderByClause)element).Items) Visit2(i); break; } case QueryElementType.OrderByItem: { Visit2(((SqlQuery.OrderByItem)element).Expression); break; } case QueryElementType.Union: Visit2(((SqlQuery.Union)element).SqlQuery); break; case QueryElementType.SqlQuery: { if (_all) { if (_visitedElements.ContainsKey(element)) return; _visitedElements.Add(element, element); } var q = (SqlQuery)element; switch (q.QueryType) { case QueryType.InsertOrUpdate : Visit2(q.Insert); Visit2(q.Update); if (q.From.Tables.Count == 0) break; goto default; case QueryType.Update : Visit2(q.Update); break; case QueryType.Delete : Visit2(q.Delete); Visit2(q.Select); break; case QueryType.Insert : Visit2(q.Insert); if (q.From.Tables.Count != 0) Visit2(q.Select); break; default : Visit2(q.Select); break; } // Visit2(q.From); // if (q.From != null && (_all || !_visitedElements.ContainsKey(q.From))) { foreach (var t in q.From.Tables) { //Visit2(t); // if (t != null && (_all || !_visitedElements.ContainsKey(t))) { Visit2(t.Source); foreach (var j in t.Joins) Visit2(j); _action2(t); if (!_all) _visitedElements.Add(t, t); } } _action2(q.From); if (!_all) _visitedElements.Add(q.From, q.From); } Visit2(q.Where); Visit2(q.GroupBy); Visit2(q.Having); Visit2(q.OrderBy); if (q.HasUnion) { foreach (var i in q.Unions) { if (i.SqlQuery == q) throw new InvalidOperationException(); Visit2(i); } } break; } } _action2(element); if (!_all) _visitedElements.Add(element, element); } #endregion #region Find IQueryElement Find<T>(IEnumerable<T> arr, FindFunc find) where T : class, IQueryElement { if (arr == null) return null; foreach (var item in arr) { var e = Find(item, find); if (e != null) return e; } return null; } public IQueryElement Find(IQueryElement element, FindFunc find) { if (element == null || find(element)) return element; switch (element.ElementType) { case QueryElementType.SqlFunction : return Find(((SqlFunction) element).Parameters, find); case QueryElementType.SqlExpression : return Find(((SqlExpression) element).Parameters, find); case QueryElementType.Join : return Find(((Join) element).JoinOns, find); case QueryElementType.Column : return Find(((SqlQuery.Column) element).Expression, find); case QueryElementType.SearchCondition : return Find(((SqlQuery.SearchCondition) element).Conditions, find); case QueryElementType.Condition : return Find(((SqlQuery.Condition) element).Predicate, find); case QueryElementType.ExprPredicate : return Find(((SqlQuery.Predicate.Expr) element).Expr1, find); case QueryElementType.NotExprPredicate : return Find(((SqlQuery.Predicate.NotExpr) element).Expr1, find); case QueryElementType.IsNullPredicate : return Find(((SqlQuery.Predicate.IsNull) element).Expr1, find); case QueryElementType.FromClause : return Find(((SqlQuery.FromClause) element).Tables, find); case QueryElementType.WhereClause : return Find(((SqlQuery.WhereClause) element).SearchCondition, find); case QueryElementType.GroupByClause : return Find(((SqlQuery.GroupByClause) element).Items, find); case QueryElementType.OrderByClause : return Find(((SqlQuery.OrderByClause) element).Items, find); case QueryElementType.OrderByItem : return Find(((SqlQuery.OrderByItem) element).Expression, find); case QueryElementType.Union : return Find(((SqlQuery.Union) element).SqlQuery, find); case QueryElementType.FuncLikePredicate : return Find(((SqlQuery.Predicate.FuncLike)element).Function, find); case QueryElementType.SqlBinaryExpression: { var bexpr = (SqlBinaryExpression)element; return Find(bexpr.Expr1, find) ?? Find(bexpr.Expr2, find); } case QueryElementType.SqlTable: { var table = (SqlTable)element; return Find(table.All, find) ?? Find(table.Fields.Values, find) ?? Find(table.Joins, find) ?? Find(table.TableArguments, find); } case QueryElementType.TableSource: { var table = (SqlQuery.TableSource)element; return Find(table.Source, find) ?? Find(table.Joins, find); } case QueryElementType.JoinedTable: { var join = (SqlQuery.JoinedTable)element; return Find(join.Table, find) ?? Find(join.Condition, find); } case QueryElementType.ExprExprPredicate: { var p = (SqlQuery.Predicate.ExprExpr)element; return Find(p.Expr1, find) ?? Find(p.Expr2, find); } case QueryElementType.LikePredicate: { var p = (SqlQuery.Predicate.Like)element; return Find(p.Expr1, find) ?? Find(p.Expr2, find) ?? Find(p.Escape, find); } case QueryElementType.BetweenPredicate: { var p = (SqlQuery.Predicate.Between)element; return Find(p.Expr1, find) ?? Find(p.Expr2, find) ?? Find(p.Expr3, find); } case QueryElementType.InSubQueryPredicate: { var p = (SqlQuery.Predicate.InSubQuery)element; return Find(p.Expr1, find) ?? Find(p.SubQuery, find); } case QueryElementType.InListPredicate: { var p = (SqlQuery.Predicate.InList)element; return Find(p.Expr1, find) ?? Find(p.Values, find); } case QueryElementType.SetExpression: { var s = (SqlQuery.SetExpression)element; return Find(s.Column, find) ?? Find(s.Expression, find); } case QueryElementType.InsertClause: { var sc = (SqlQuery.InsertClause)element; return Find(sc.Into, find) ?? Find(sc.Items, find); } case QueryElementType.UpdateClause: { var sc = (SqlQuery.UpdateClause)element; return Find(sc.Table, find) ?? Find(sc.Items, find) ?? Find(sc.Keys, find); } case QueryElementType.DeleteClause: { var sc = (SqlQuery.DeleteClause)element; return Find(sc.Table, find); } case QueryElementType.SelectClause: { var sc = (SqlQuery.SelectClause)element; return Find(sc.TakeValue, find) ?? Find(sc.SkipValue, find) ?? Find(sc.Columns, find); } case QueryElementType.SqlQuery: { var q = (SqlQuery)element; return Find(q.Select, find) ?? (q.IsInsert ? Find(q.Insert, find) : null) ?? (q.IsUpdate ? Find(q.Update, find) : null) ?? Find(q.From, find) ?? Find(q.Where, find) ?? Find(q.GroupBy, find) ?? Find(q.Having, find) ?? Find(q.OrderBy, find) ?? (q.HasUnion ? Find(q.Unions, find) : null); } } return null; } #endregion #region Convert public T Convert<T>(T element, ConvertFunc action) where T : class, IQueryElement { _visitedElements.Clear(); return (T)ConvertInternal(element, action) ?? element; } IQueryElement ConvertInternal(IQueryElement element, ConvertFunc action) { if (element == null) return null; IQueryElement newElement; if (_visitedElements.TryGetValue(element, out newElement)) return newElement; switch (element.ElementType) { case QueryElementType.SqlFunction: { var func = (SqlFunction)element; var parms = Convert(func.Parameters, action); if (parms != null && !ReferenceEquals(parms, func.Parameters)) newElement = new SqlFunction(func.SystemType, func.Name, func.Precedence, parms); break; } case QueryElementType.SqlExpression: { var expr = (SqlExpression)element; var parameter = Convert(expr.Parameters, action); if (parameter != null && !ReferenceEquals(parameter, expr.Parameters)) newElement = new SqlExpression(expr.SystemType, expr.Expr, expr.Precedence, parameter); break; } case QueryElementType.SqlBinaryExpression: { var bexpr = (SqlBinaryExpression)element; var expr1 = (ISqlExpression)ConvertInternal(bexpr.Expr1, action); var expr2 = (ISqlExpression)ConvertInternal(bexpr.Expr2, action); if (expr1 != null && !ReferenceEquals(expr1, bexpr.Expr1) || expr2 != null && !ReferenceEquals(expr2, bexpr.Expr2)) newElement = new SqlBinaryExpression(bexpr.SystemType, expr1 ?? bexpr.Expr1, bexpr.Operation, expr2 ?? bexpr.Expr2, bexpr.Precedence); break; } case QueryElementType.SqlTable: { var table = (SqlTable)element; var fields1 = ToArray(table.Fields); var fields2 = Convert(fields1, action, f => new SqlField(f)); var joins = Convert(table.Joins, action, j => j.Clone()); var targs = table.TableArguments == null ? null : Convert(table.TableArguments, action); var fe = fields2 == null || ReferenceEquals(fields1, fields2); var je = joins == null || ReferenceEquals(table.Joins, joins); var ta = ReferenceEquals(table.TableArguments, targs); if (!fe || !je || !ta) { if (fe) { fields2 = fields1; for (var i = 0; i < fields2.Length; i++) { var field = fields2[i]; fields2[i] = new SqlField(field); _visitedElements[field] = fields2[i]; } } newElement = new SqlTable(table, fields2, joins ?? table.Joins, targs ?? table.TableArguments); _visitedElements[((SqlTable)newElement).All] = table.All; } break; } case QueryElementType.Join: { var join = (Join)element; var ons = Convert(join.JoinOns, action); if (ons != null && !ReferenceEquals(join.JoinOns, ons)) newElement = new Join(join.TableName, join.Alias, ons); break; } case QueryElementType.Column: { var col = (SqlQuery.Column)element; var expr = (ISqlExpression)ConvertInternal(col.Expression, action); IQueryElement parent; _visitedElements.TryGetValue(col.Parent, out parent); if (parent != null || expr != null && !ReferenceEquals(expr, col.Expression)) newElement = new SqlQuery.Column(parent == null ? col.Parent : (SqlQuery)parent, expr ?? col.Expression, col._alias); break; } case QueryElementType.TableSource: { var table = (SqlQuery.TableSource)element; var source = (ISqlTableSource)ConvertInternal(table.Source, action); var joins = Convert(table.Joins, action); if (source != null && !ReferenceEquals(source, table.Source) || joins != null && !ReferenceEquals(table.Joins, joins)) newElement = new SqlQuery.TableSource(source ?? table.Source, table._alias, joins ?? table.Joins); break; } case QueryElementType.JoinedTable: { var join = (SqlQuery.JoinedTable)element; var table = (SqlQuery.TableSource) ConvertInternal(join.Table, action); var cond = (SqlQuery.SearchCondition)ConvertInternal(join.Condition, action); if (table != null && !ReferenceEquals(table, join.Table) || cond != null && !ReferenceEquals(cond, join.Condition)) newElement = new SqlQuery.JoinedTable(join.JoinType, table ?? join.Table, join.IsWeak, cond ?? join.Condition); break; } case QueryElementType.SearchCondition: { var sc = (SqlQuery.SearchCondition)element; var conds = Convert(sc.Conditions, action); if (conds != null && !ReferenceEquals(sc.Conditions, conds)) newElement = new SqlQuery.SearchCondition(conds); break; } case QueryElementType.Condition: { var c = (SqlQuery.Condition)element; var p = (ISqlPredicate)ConvertInternal(c.Predicate, action); if (p != null && !ReferenceEquals(c.Predicate, p)) newElement = new SqlQuery.Condition(c.IsNot, p, c.IsOr); break; } case QueryElementType.ExprPredicate: { var p = (SqlQuery.Predicate.Expr)element; var e = (ISqlExpression)ConvertInternal(p.Expr1, action); if (e != null && !ReferenceEquals(p.Expr1, e)) newElement = new SqlQuery.Predicate.Expr(e, p.Precedence); break; } case QueryElementType.NotExprPredicate: { var p = (SqlQuery.Predicate.NotExpr)element; var e = (ISqlExpression)ConvertInternal(p.Expr1, action); if (e != null && !ReferenceEquals(p.Expr1, e)) newElement = new SqlQuery.Predicate.NotExpr(e, p.IsNot, p.Precedence); break; } case QueryElementType.ExprExprPredicate: { var p = (SqlQuery.Predicate.ExprExpr)element; var e1 = (ISqlExpression)ConvertInternal(p.Expr1, action); var e2 = (ISqlExpression)ConvertInternal(p.Expr2, action); if (e1 != null && !ReferenceEquals(p.Expr1, e1) || e2 != null && !ReferenceEquals(p.Expr2, e2)) newElement = new SqlQuery.Predicate.ExprExpr(e1 ?? p.Expr1, p.Operator, e2 ?? p.Expr2); break; } case QueryElementType.LikePredicate: { var p = (SqlQuery.Predicate.Like)element; var e1 = (ISqlExpression)ConvertInternal(p.Expr1, action); var e2 = (ISqlExpression)ConvertInternal(p.Expr2, action); var es = (ISqlExpression)ConvertInternal(p.Escape, action); if (e1 != null && !ReferenceEquals(p.Expr1, e1) || e2 != null && !ReferenceEquals(p.Expr2, e2) || es != null && !ReferenceEquals(p.Escape, es)) newElement = new SqlQuery.Predicate.Like(e1 ?? p.Expr1, p.IsNot, e2 ?? p.Expr2, es ?? p.Escape); break; } case QueryElementType.BetweenPredicate: { var p = (SqlQuery.Predicate.Between)element; var e1 = (ISqlExpression)ConvertInternal(p.Expr1, action); var e2 = (ISqlExpression)ConvertInternal(p.Expr2, action); var e3 = (ISqlExpression)ConvertInternal(p.Expr3, action); if (e1 != null && !ReferenceEquals(p.Expr1, e1) || e2 != null && !ReferenceEquals(p.Expr2, e2) || e3 != null && !ReferenceEquals(p.Expr3, e3)) newElement = new SqlQuery.Predicate.Between(e1 ?? p.Expr1, p.IsNot, e2 ?? p.Expr2, e3 ?? p.Expr3); break; } case QueryElementType.IsNullPredicate: { var p = (SqlQuery.Predicate.IsNull)element; var e = (ISqlExpression)ConvertInternal(p.Expr1, action); if (e != null && !ReferenceEquals(p.Expr1, e)) newElement = new SqlQuery.Predicate.IsNull(e, p.IsNot); break; } case QueryElementType.InSubQueryPredicate: { var p = (SqlQuery.Predicate.InSubQuery)element; var e = (ISqlExpression)ConvertInternal(p.Expr1, action); var q = (SqlQuery)ConvertInternal(p.SubQuery, action); if (e != null && !ReferenceEquals(p.Expr1, e) || q != null && !ReferenceEquals(p.SubQuery, q)) newElement = new SqlQuery.Predicate.InSubQuery(e ?? p.Expr1, p.IsNot, q ?? p.SubQuery); break; } case QueryElementType.InListPredicate: { var p = (SqlQuery.Predicate.InList)element; var e = (ISqlExpression)ConvertInternal(p.Expr1, action); var v = Convert(p.Values, action); if (e != null && !ReferenceEquals(p.Expr1, e) || v != null && !ReferenceEquals(p.Values, v)) newElement = new SqlQuery.Predicate.InList(e ?? p.Expr1, p.IsNot, v ?? p.Values); break; } case QueryElementType.FuncLikePredicate: { var p = (SqlQuery.Predicate.FuncLike)element; var f = (SqlFunction)ConvertInternal(p.Function, action); if (f != null && !ReferenceEquals(p.Function, f)) newElement = new SqlQuery.Predicate.FuncLike(f); break; } case QueryElementType.SetExpression: { var s = (SqlQuery.SetExpression)element; var c = (ISqlExpression)ConvertInternal(s.Column, action); var e = (ISqlExpression)ConvertInternal(s.Expression, action); if (c != null && !ReferenceEquals(s.Column, c) || e != null && !ReferenceEquals(s.Expression, e)) newElement = new SqlQuery.SetExpression(c ?? s.Column, e ?? s.Expression); break; } case QueryElementType.InsertClause: { var s = (SqlQuery.InsertClause)element; var t = s.Into != null ? (SqlTable)ConvertInternal(s.Into, action) : null; var i = Convert(s.Items, action); if (t != null && !ReferenceEquals(s.Into, t) || i != null && !ReferenceEquals(s.Items, i)) { var sc = new SqlQuery.InsertClause { Into = t ?? s.Into }; sc.Items.AddRange(i ?? s.Items); sc.WithIdentity = s.WithIdentity; newElement = sc; } break; } case QueryElementType.UpdateClause: { var s = (SqlQuery.UpdateClause)element; var t = s.Table != null ? (SqlTable)ConvertInternal(s.Table, action) : null; var i = Convert(s.Items, action); var k = Convert(s.Keys, action); if (t != null && !ReferenceEquals(s.Table, t) || i != null && !ReferenceEquals(s.Items, i) || k != null && !ReferenceEquals(s.Keys, k)) { var sc = new SqlQuery.UpdateClause { Table = t ?? s.Table }; sc.Items.AddRange(i ?? s.Items); sc.Keys. AddRange(k ?? s.Keys); newElement = sc; } break; } case QueryElementType.DeleteClause: { var s = (SqlQuery.DeleteClause)element; var t = s.Table != null ? (SqlTable)ConvertInternal(s.Table, action) : null; if (t != null && !ReferenceEquals(s.Table, t)) { newElement = new SqlQuery.DeleteClause { Table = t ?? s.Table }; } break; } case QueryElementType.SelectClause: { var sc = (SqlQuery.SelectClause)element; var cols = Convert(sc.Columns, action); var take = (ISqlExpression)ConvertInternal(sc.TakeValue, action); var skip = (ISqlExpression)ConvertInternal(sc.SkipValue, action); IQueryElement parent; _visitedElements.TryGetValue(sc.SqlQuery, out parent); if (parent != null || cols != null && !ReferenceEquals(sc.Columns, cols) || take != null && !ReferenceEquals(sc.TakeValue, take) || skip != null && !ReferenceEquals(sc.SkipValue, skip)) { newElement = new SqlQuery.SelectClause(sc.IsDistinct, take ?? sc.TakeValue, skip ?? sc.SkipValue, cols ?? sc.Columns); ((SqlQuery.SelectClause)newElement).SetSqlQuery((SqlQuery)parent); } break; } case QueryElementType.FromClause: { var fc = (SqlQuery.FromClause)element; var ts = Convert(fc.Tables, action); IQueryElement parent; _visitedElements.TryGetValue(fc.SqlQuery, out parent); if (parent != null || ts != null && !ReferenceEquals(fc.Tables, ts)) { newElement = new SqlQuery.FromClause(ts ?? fc.Tables); ((SqlQuery.FromClause)newElement).SetSqlQuery((SqlQuery)parent); } break; } case QueryElementType.WhereClause: { var wc = (SqlQuery.WhereClause)element; var cond = (SqlQuery.SearchCondition)ConvertInternal(wc.SearchCondition, action); IQueryElement parent; _visitedElements.TryGetValue(wc.SqlQuery, out parent); if (parent != null || cond != null && !ReferenceEquals(wc.SearchCondition, cond)) { newElement = new SqlQuery.WhereClause(cond ?? wc.SearchCondition); ((SqlQuery.WhereClause)newElement).SetSqlQuery((SqlQuery)parent); } break; } case QueryElementType.GroupByClause: { var gc = (SqlQuery.GroupByClause)element; var es = Convert(gc.Items, action); IQueryElement parent; _visitedElements.TryGetValue(gc.SqlQuery, out parent); if (parent != null || es != null && !ReferenceEquals(gc.Items, es)) { newElement = new SqlQuery.GroupByClause(es ?? gc.Items); ((SqlQuery.GroupByClause)newElement).SetSqlQuery((SqlQuery)parent); } break; } case QueryElementType.OrderByClause: { var oc = (SqlQuery.OrderByClause)element; var es = Convert(oc.Items, action); IQueryElement parent; _visitedElements.TryGetValue(oc.SqlQuery, out parent); if (parent != null || es != null && !ReferenceEquals(oc.Items, es)) { newElement = new SqlQuery.OrderByClause(es ?? oc.Items); ((SqlQuery.OrderByClause)newElement).SetSqlQuery((SqlQuery)parent); } break; } case QueryElementType.OrderByItem: { var i = (SqlQuery.OrderByItem)element; var e = (ISqlExpression)ConvertInternal(i.Expression, action); if (e != null && !ReferenceEquals(i.Expression, e)) newElement = new SqlQuery.OrderByItem(e, i.IsDescending); break; } case QueryElementType.Union: { var u = (SqlQuery.Union)element; var q = (SqlQuery)ConvertInternal(u.SqlQuery, action); if (q != null && !ReferenceEquals(u.SqlQuery, q)) newElement = new SqlQuery.Union(q, u.IsAll); break; } case QueryElementType.SqlQuery: { var q = (SqlQuery)element; IQueryElement parent = null; var doConvert = q.ParentSql != null && !_visitedElements.TryGetValue(q.ParentSql, out parent); if (!doConvert) { doConvert = null != Find(q, e => { if (_visitedElements.ContainsKey(e) && _visitedElements[e] != e) return true; var ret = action(e); if (ret != null && !ReferenceEquals(e, ret)) { _visitedElements.Add(e, ret); return true; } return false; }); } if (!doConvert) break; var nq = new SqlQuery { QueryType = q.QueryType }; _visitedElements.Add(q, nq); var fc = (SqlQuery.FromClause) ConvertInternal(q.From, action) ?? q.From; var sc = (SqlQuery.SelectClause) ConvertInternal(q.Select, action) ?? q.Select; var ic = q.IsInsert ? ((SqlQuery.InsertClause)ConvertInternal(q.Insert, action) ?? q.Insert) : null; var uc = q.IsUpdate ? ((SqlQuery.UpdateClause)ConvertInternal(q.Update, action) ?? q.Update) : null; var dc = q.IsDelete ? ((SqlQuery.DeleteClause)ConvertInternal(q.Delete, action) ?? q.Delete) : null; var wc = (SqlQuery.WhereClause) ConvertInternal(q.Where, action) ?? q.Where; var gc = (SqlQuery.GroupByClause)ConvertInternal(q.GroupBy, action) ?? q.GroupBy; var hc = (SqlQuery.WhereClause) ConvertInternal(q.Having, action) ?? q.Having; var oc = (SqlQuery.OrderByClause)ConvertInternal(q.OrderBy, action) ?? q.OrderBy; var us = q.HasUnion ? Convert(q.Unions, action) : q.Unions; var ps = new List<SqlParameter>(q.Parameters.Count); foreach (var p in q.Parameters) { IQueryElement e; if (_visitedElements.TryGetValue(p, out e)) { if (e == null) ps.Add(p); else if (e is SqlParameter) ps.Add((SqlParameter)e); } } nq.Init(ic, uc, dc, sc, fc, wc, gc, hc, oc, us, (SqlQuery)parent, q.IsParameterDependent, ps); _visitedElements[q] = action(nq) ?? nq; return nq; } } newElement = newElement == null ? action(element) : (action(newElement) ?? newElement); _visitedElements.Add(element, newElement); return newElement; } static TE[] ToArray<TK,TE>(IDictionary<TK,TE> dic) { var es = new TE[dic.Count]; var i = 0; foreach (var e in dic.Values) es[i++] = e; return es; } delegate T Clone<T>(T obj); T[] Convert<T>(T[] arr, ConvertFunc action) where T : class, IQueryElement { return Convert(arr, action, null); } T[] Convert<T>(T[] arr1, ConvertFunc action, Clone<T> clone) where T : class, IQueryElement { T[] arr2 = null; for (var i = 0; i < arr1.Length; i++) { var elem1 = arr1[i]; var elem2 = (T)ConvertInternal(elem1, action); if (elem2 != null && !ReferenceEquals(elem1, elem2)) { if (arr2 == null) { arr2 = new T[arr1.Length]; for (var j = 0; j < i; j++) arr2[j] = clone == null ? arr1[j] : clone(arr1[j]); } arr2[i] = elem2; } else if (arr2 != null) arr2[i] = clone == null ? elem1 : clone(elem1); } return arr2; } List<T> Convert<T>(List<T> list, ConvertFunc action) where T : class, IQueryElement { return Convert(list, action, null); } List<T> Convert<T>(List<T> list1, ConvertFunc action, Clone<T> clone) where T : class, IQueryElement { List<T> list2 = null; for (var i = 0; i < list1.Count; i++) { var elem1 = list1[i]; var elem2 = (T)ConvertInternal(elem1, action); if (elem2 != null && !ReferenceEquals(elem1, elem2)) { if (list2 == null) { list2 = new List<T>(list1.Count); for (var j = 0; j < i; j++) list2.Add(clone == null ? list1[j] : clone(list1[j])); } list2.Add(elem2); } else if (list2 != null) list2.Add(clone == null ? elem1 : clone(elem1)); } return list2; } #endregion } }