diff --git a/Marr.Data/QGen/Dialects/Dialect.cs b/Marr.Data/QGen/Dialects/Dialect.cs index 18d20afda..4d873e87a 100644 --- a/Marr.Data/QGen/Dialects/Dialect.cs +++ b/Marr.Data/QGen/Dialects/Dialect.cs @@ -55,5 +55,20 @@ namespace Marr.Data.QGen.Dialects return true; } } + + public virtual string StartsWithFormat + { + get { return "({0} LIKE {1} + '%')"; } + } + + public virtual string EndsWithFormat + { + get { return "({0} LIKE '%' + {1})"; } + } + + public virtual string ContainsFormat + { + get { return "({0} LIKE '%' + {1} + '%')"; } + } } } diff --git a/Marr.Data/QGen/Dialects/SqliteDialect.cs b/Marr.Data/QGen/Dialects/SqliteDialect.cs index 226675da7..079563992 100644 --- a/Marr.Data/QGen/Dialects/SqliteDialect.cs +++ b/Marr.Data/QGen/Dialects/SqliteDialect.cs @@ -14,5 +14,20 @@ namespace Marr.Data.QGen.Dialects return "SELECT last_insert_rowid();"; } } + + public override string StartsWithFormat + { + get { return "({0} LIKE {1} || '%')"; } + } + + public override string EndsWithFormat + { + get { return "({0} LIKE '%' || {1})"; } + } + + public override string ContainsFormat + { + get { return "({0} LIKE '%' || {1} || '%')"; } + } } } diff --git a/Marr.Data/QGen/QueryBuilder.cs b/Marr.Data/QGen/QueryBuilder.cs index 6a99c4c19..fe3d86097 100644 --- a/Marr.Data/QGen/QueryBuilder.cs +++ b/Marr.Data/QGen/QueryBuilder.cs @@ -20,25 +20,31 @@ namespace Marr.Data.QGen { #region - Private Members - - private Dialects.Dialect _dialect; private DataMapper _db; + private Dialects.Dialect _dialect; private TableCollection _tables; private WhereBuilder _whereBuilder; private SortBuilder _sortBuilder; - private bool _useAltName = false; private bool _isGraph = false; - private string _queryText; - private List _childrenToLoad; + private bool _isFromView = false; + private bool _isFromTable = false; + private bool _isJoin = false; + private bool _isManualQuery = false; private bool _enablePaging = false; private int _skip; private int _take; + private string _queryText; + private List _childrenToLoad; private SortBuilder SortBuilder { get { // Lazy load if (_sortBuilder == null) - _sortBuilder = new SortBuilder(this, _db, _whereBuilder, _dialect, _tables, _useAltName); + { + bool useAltNames = _isFromView || _isGraph || _isJoin; + _sortBuilder = new SortBuilder(this, _db, _whereBuilder, _dialect, _tables, useAltNames); + } return _sortBuilder; } @@ -98,14 +104,19 @@ namespace Marr.Data.QGen if (string.IsNullOrEmpty(viewName)) throw new ArgumentNullException("view"); - _useAltName = true; + _isFromView = true; // Replace the base table with a view with tables - View view = new View(viewName, _tables.ToArray()); - _tables.ReplaceBaseTable(view); + if (_tables[0] is View) + { + (_tables[0] as View).Name = viewName; + } + else + { + View view = new View(viewName, _tables.ToArray()); + _tables.ReplaceBaseTable(view); + } - //// Override the base table name - //_tables[0].Name = view; return this; } @@ -118,7 +129,7 @@ namespace Marr.Data.QGen if (string.IsNullOrEmpty(table)) throw new ArgumentNullException("view"); - _useAltName = false; + _isFromTable = true; // Override the base table name _tables[0].Name = table; @@ -130,6 +141,7 @@ namespace Marr.Data.QGen /// public virtual QueryBuilder QueryText(string queryText) { + _isManualQuery = true; _queryText = queryText; return this; } @@ -177,7 +189,6 @@ namespace Marr.Data.QGen _tables.ReplaceBaseTable(view); _isGraph = true; - _useAltName = true; return this; } @@ -237,7 +248,8 @@ namespace Marr.Data.QGen // Generate a row count query string where = _whereBuilder != null ? _whereBuilder.ToString() : string.Empty; - IQuery query = QueryFactory.CreateRowCountSelectQuery(_tables, _db, where, SortBuilder, _useAltName); + bool useAltNames = _isFromView || _isGraph || _isJoin; + IQuery query = QueryFactory.CreateRowCountSelectQuery(_tables, _db, where, SortBuilder, useAltNames); string queryText = query.Generate(); _db.SqlMode = SqlModes.Text; @@ -255,15 +267,17 @@ namespace Marr.Data.QGen { SqlModes previousSqlMode = _db.SqlMode; + ValidateQuery(); + BuildQueryOrAppendClauses(); - if (_isGraph) + if (_isGraph || _isJoin) { _results = (List)_db.QueryToGraph(_queryText, EntGraph, _childrenToLoad); } else { - _results = (List)_db.Query(_queryText, _results, _useAltName); + _results = (List)_db.Query(_queryText, _results, _isFromView); } // Return to previous sql mode @@ -272,6 +286,33 @@ namespace Marr.Data.QGen return _results; } + private void ValidateQuery() + { + if (_isManualQuery && _isFromView) + throw new InvalidOperationException("Cannot use FromView in conjunction with QueryText"); + + if (_isManualQuery && _isFromTable) + throw new InvalidOperationException("Cannot use FromTable in conjunction with QueryText"); + + if (_isManualQuery && _isJoin) + throw new InvalidOperationException("Cannot use Join in conjuntion with QueryText"); + + if (_isManualQuery && _enablePaging) + throw new InvalidOperationException("Cannot use Page, Skip or Take in conjunction with QueryText"); + + if (_isJoin && _isFromView) + throw new InvalidOperationException("Cannot use FromView in conjunction with Join"); + + if (_isJoin && _isFromTable) + throw new InvalidOperationException("Cannot use FromView in conjunction with Join"); + + if (_isJoin && _isGraph) + throw new InvalidOperationException("Cannot use Graph in conjunction with Join"); + + if (_isFromView && _isFromTable) + throw new InvalidOperationException("Cannot use FromView in conjunction with FromTable"); + } + private void BuildQueryOrAppendClauses() { if (_queryText == null) @@ -302,14 +343,16 @@ namespace Marr.Data.QGen // Generate a query string where = _whereBuilder != null ? _whereBuilder.ToString() : string.Empty; + bool useAltNames = _isFromView || _isGraph || _isJoin; + IQuery query = null; if (_enablePaging) { - query = QueryFactory.CreatePagingSelectQuery(_tables, _db, where, SortBuilder, _useAltName, _skip, _take); + query = QueryFactory.CreatePagingSelectQuery(_tables, _db, where, SortBuilder, useAltNames, _skip, _take); } else { - query = QueryFactory.CreateSelectQuery(_tables, _db, where, SortBuilder, _useAltName); + query = QueryFactory.CreateSelectQuery(_tables, _db, where, SortBuilder, useAltNames); } _queryText = query.Generate(); @@ -324,10 +367,11 @@ namespace Marr.Data.QGen private ColumnMapCollection GetColumns(IEnumerable entitiesToLoad) { // If QueryToGraph and no child load entities are specified, load all children - bool loadAllChildren = _useAltName && entitiesToLoad == null; + bool useAltNames = _isFromView || _isGraph || _isJoin; + bool loadAllChildren = useAltNames && entitiesToLoad == null; // If Query - if (!_useAltName) + if (!useAltNames) { return MapRepository.Instance.GetColumns(typeof(T)); } @@ -359,13 +403,17 @@ namespace Marr.Data.QGen public virtual SortBuilder Where(Expression> filterExpression) { - _whereBuilder = new WhereBuilder(_db.Command, _dialect, filterExpression, _tables, _useAltName, true); + bool useAltNames = _isFromView || _isGraph; + bool addTablePrefixToColumns = true; + _whereBuilder = new WhereBuilder(_db.Command, _dialect, filterExpression, _tables, useAltNames, addTablePrefixToColumns); return SortBuilder; } public virtual SortBuilder Where(Expression> filterExpression) { - _whereBuilder = new WhereBuilder(_db.Command, _dialect, filterExpression, _tables, _useAltName, true); + bool useAltNames = _isFromView || _isGraph; + bool addTablePrefixToColumns = true; + _whereBuilder = new WhereBuilder(_db.Command, _dialect, filterExpression, _tables, useAltNames, addTablePrefixToColumns); return SortBuilder; } @@ -379,7 +427,8 @@ namespace Marr.Data.QGen whereClause = whereClause.Insert(0, " WHERE "); } - _whereBuilder = new WhereBuilder(whereClause, _useAltName); + bool useAltNames = _isFromView || _isGraph || _isJoin; + _whereBuilder = new WhereBuilder(whereClause, useAltNames); return SortBuilder; } @@ -491,20 +540,21 @@ namespace Marr.Data.QGen public virtual QueryBuilder Join(JoinType joinType, Expression>> rightEntity, Expression> filterExpression) { + _isJoin = true; MemberInfo rightMember = (rightEntity.Body as MemberExpression).Member; return this.Join(joinType, rightMember, filterExpression); } public virtual QueryBuilder Join(JoinType joinType, Expression> rightEntity, Expression> filterExpression) { + _isJoin = true; MemberInfo rightMember = (rightEntity.Body as MemberExpression).Member; return this.Join(joinType, rightMember, filterExpression); } public virtual QueryBuilder Join(JoinType joinType, MemberInfo rightMember, Expression> filterExpression) { - _useAltName = true; - _isGraph = true; + _isJoin = true; if (!_childrenToLoad.ContainsMember(rightMember)) _childrenToLoad.Add(rightMember); diff --git a/Marr.Data/QGen/SortBuilder.cs b/Marr.Data/QGen/SortBuilder.cs index 69fc7959d..0865381a5 100644 --- a/Marr.Data/QGen/SortBuilder.cs +++ b/Marr.Data/QGen/SortBuilder.cs @@ -170,15 +170,6 @@ namespace Marr.Data.QGen #endregion - #region - Count - - - public virtual int Count() - { - return _baseBuilder.GetRowCount(); - } - - #endregion - #region - ToList / ToString / BuildQuery - public virtual List ToList() @@ -218,7 +209,7 @@ namespace Marr.Data.QGen string columnName = DataHelper.GetColumnName(sort.DeclaringType, sort.PropertyName, useAltName); - if (!_useAltName) + if (!useAltName) sb.Append(_dialect.CreateToken(string.Format("{0}.{1}", table.Alias, columnName))); else diff --git a/Marr.Data/QGen/SqlitePagingQueryDecorator.cs b/Marr.Data/QGen/SqlitePagingQueryDecorator.cs index a0cb66c0b..a152ede50 100644 --- a/Marr.Data/QGen/SqlitePagingQueryDecorator.cs +++ b/Marr.Data/QGen/SqlitePagingQueryDecorator.cs @@ -30,6 +30,19 @@ namespace Marr.Data.QGen public string Generate() { + if (_innerQuery.IsView || _innerQuery.IsJoin) + { + return ComplexPaging(); + } + else + { + return SimplePaging(); + } + } + + private string SimplePaging() + { + // Create paged query StringBuilder sql = new StringBuilder(); _innerQuery.BuildSelectClause(sql); @@ -42,6 +55,28 @@ namespace Marr.Data.QGen return sql.ToString(); } + private string ComplexPaging() + { + var baseTable = _innerQuery.Tables.First(); + + + StringBuilder sql = new StringBuilder(); + + _innerQuery.BuildSelectClause(sql); + sql.Append(" FROM ("); + BuildSimpleInnerSelect(sql); + _innerQuery.BuildFromClause(sql); + _innerQuery.BuildWhereClause(sql); + sql.AppendLine(String.Format("LIMIT {0},{1}", _skip, _take)); + sql.AppendFormat(") AS {0} ", _innerQuery.Dialect.CreateToken(baseTable.Alias)); + + _innerQuery.BuildJoinClauses(sql); + + _innerQuery.BuildOrderClause(sql); + + return sql.ToString(); + } + public void BuildSelectClause(StringBuilder sql) { List appended = new List(); @@ -86,5 +121,26 @@ namespace Marr.Data.QGen } } } + + private void BuildSimpleInnerSelect(StringBuilder sql) + { + sql.Append("SELECT "); + int startIndex = sql.Length; + + // COLUMNS + var join = _innerQuery.Tables.First(); + + for (int i = 0; i < join.Columns.Count; i++) + { + var c = join.Columns[i]; + + if (sql.Length > startIndex) + sql.Append(","); + + string token = c.ColumnInfo.Name; + sql.Append(_innerQuery.Dialect.CreateToken(token)); + } + + } } } diff --git a/Marr.Data/QGen/WhereBuilder.cs b/Marr.Data/QGen/WhereBuilder.cs index b76db2a20..012fa187e 100644 --- a/Marr.Data/QGen/WhereBuilder.cs +++ b/Marr.Data/QGen/WhereBuilder.cs @@ -24,7 +24,7 @@ namespace Marr.Data.QGen private MapRepository _repos; private DbCommand _command; private string _paramPrefix; - private bool isLeftSide = true; + private bool _isLeftSide = true; protected bool _useAltName; protected Dialect _dialect; protected StringBuilder _sb; @@ -67,12 +67,12 @@ namespace Marr.Data.QGen { _sb.Append("("); - isLeftSide = true; + _isLeftSide = true; Visit(expression.Left); - _sb.AppendFormat(" {0} ", Decode(expression.NodeType)); + _sb.AppendFormat(" {0} ", Decode(expression)); - isLeftSide = false; + _isLeftSide = false; Visit(expression.Right); _sb.Append(")"); @@ -107,7 +107,7 @@ namespace Marr.Data.QGen protected override Expression VisitMemberAccess(MemberExpression expression) { - if (isLeftSide) + if (_isLeftSide) { string fqColumn = GetFullyQualifiedColumnName(expression.Member, expression.Expression.Type); _sb.Append(fqColumn); @@ -127,12 +127,20 @@ namespace Marr.Data.QGen protected override Expression VisitConstant(ConstantExpression expression) { - // Add parameter to Command.Parameters - string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString()); + if (expression.Value != null) + { + // Add parameter to Command.Parameters + string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString()); - _sb.Append(paramName); + _sb.Append(paramName); + + var parameter = new ParameterChainMethods(_command, paramName, expression.Value).Parameter; + } + else + { + _sb.Append("NULL"); + } - var parameter = new ParameterChainMethods(_command, paramName, expression.Value).Parameter; return expression; } @@ -182,12 +190,7 @@ namespace Marr.Data.QGen } string columnName = DataHelper.GetColumnName(declaringType, member.Name, _useAltName); - - if (!_useAltName) - return _dialect.CreateToken(string.Format("{0}.{1}", table.Alias, columnName)); - - else - return _dialect.CreateToken(string.Format("{0}", columnName)); + return _dialect.CreateToken(string.Format("{0}.{1}", table.Alias, columnName)); } else { @@ -196,9 +199,22 @@ namespace Marr.Data.QGen } } - private string Decode(ExpressionType expType) + private string Decode(BinaryExpression expression) { - switch (expType) + bool isRightSideNullConstant = expression.Right.NodeType == + ExpressionType.Constant && + ((ConstantExpression)expression.Right).Value == null; + + if (isRightSideNullConstant) + { + switch (expression.NodeType) + { + case ExpressionType.Equal: return "IS"; + case ExpressionType.NotEqual: return "IS NOT"; + } + } + + switch (expression.NodeType) { case ExpressionType.AndAlso: return "AND"; case ExpressionType.And: return "AND"; @@ -210,7 +226,7 @@ namespace Marr.Data.QGen case ExpressionType.NotEqual: return "<>"; case ExpressionType.OrElse: return "OR"; case ExpressionType.Or: return "OR"; - default: throw new NotSupportedException(string.Format("{0} statement is not supported", expType.ToString())); + default: throw new NotSupportedException(string.Format("{0} statement is not supported", expression.NodeType.ToString())); } } @@ -223,7 +239,7 @@ namespace Marr.Data.QGen MemberExpression memberExp = (body.Object as MemberExpression); string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type); - _sb.AppendFormat("({0} LIKE '%' + {1} + '%')", fqColumn, paramName); + _sb.AppendFormat(_dialect.ContainsFormat, fqColumn, paramName); } private void Write_StartsWith(MethodCallExpression body) @@ -235,7 +251,7 @@ namespace Marr.Data.QGen MemberExpression memberExp = (body.Object as MemberExpression); string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type); - _sb.AppendFormat("({0} LIKE {1} + '%')", fqColumn, paramName); + _sb.AppendFormat(_dialect.StartsWithFormat, fqColumn, paramName); } private void Write_EndsWith(MethodCallExpression body) @@ -247,7 +263,7 @@ namespace Marr.Data.QGen MemberExpression memberExp = (body.Object as MemberExpression); string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type); - _sb.AppendFormat("({0} LIKE '%' + {1})", fqColumn, paramName); + _sb.AppendFormat(_dialect.EndsWithFormat, fqColumn, paramName); } /// diff --git a/Marr.Data/Reflection/CachedReflectionStrategy.cs b/Marr.Data/Reflection/CachedReflectionStrategy.cs index 277bebe57..2b4be4b76 100644 --- a/Marr.Data/Reflection/CachedReflectionStrategy.cs +++ b/Marr.Data/Reflection/CachedReflectionStrategy.cs @@ -72,7 +72,7 @@ namespace Marr.Data.Reflection } /// - /// Instantiantes a type using the FastReflector library for increased speed. + /// Instantiantes a type using the Fasterflect library for increased speed. /// /// /// diff --git a/Marr.Data/Reflection/ReflectionHelper.cs b/Marr.Data/Reflection/ReflectionHelper.cs index 1ba3e9de9..c7ba453c6 100644 --- a/Marr.Data/Reflection/ReflectionHelper.cs +++ b/Marr.Data/Reflection/ReflectionHelper.cs @@ -16,9 +16,7 @@ License along with this library. If not, see . */ using System; using System.Reflection; -// ReSharper disable CheckNamespace namespace Marr.Data -// ReSharper restore CheckNamespace { public class ReflectionHelper {