diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs index 2814d455d1..708097dea9 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs @@ -129,7 +129,7 @@ namespace Umbraco.Core.Persistence public static Sql OrderByDescending(this Sql sql, params object[] columns) { - return sql.Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x.ToString() + " DESC").ToArray()))); + return sql.Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x + " DESC").ToArray()))); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs index 0fe28d085f..394f6efa81 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs @@ -1,10 +1,496 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Text; using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Querying { + internal abstract class BaseExpressionHelper : BaseExpressionHelper + { + private string sep = " "; + + protected abstract string VisitMemberAccess(MemberExpression m); + + protected internal virtual string Visit(Expression exp) + { + + if (exp == null) return string.Empty; + switch (exp.NodeType) + { + case ExpressionType.Lambda: + return VisitLambda(exp as LambdaExpression); + case ExpressionType.MemberAccess: + return VisitMemberAccess(exp as MemberExpression); + case ExpressionType.Constant: + return VisitConstant(exp as ConstantExpression); + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.Divide: + case ExpressionType.Modulo: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.Coalesce: + case ExpressionType.ArrayIndex: + case ExpressionType.RightShift: + case ExpressionType.LeftShift: + case ExpressionType.ExclusiveOr: + return VisitBinary(exp as BinaryExpression); + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + case ExpressionType.ArrayLength: + case ExpressionType.Quote: + case ExpressionType.TypeAs: + return VisitUnary(exp as UnaryExpression); + case ExpressionType.Parameter: + return VisitParameter(exp as ParameterExpression); + case ExpressionType.Call: + return VisitMethodCall(exp as MethodCallExpression); + case ExpressionType.New: + return VisitNew(exp as NewExpression); + case ExpressionType.NewArrayInit: + case ExpressionType.NewArrayBounds: + return VisitNewArray(exp as NewArrayExpression); + default: + return exp.ToString(); + } + } + + protected virtual string VisitLambda(LambdaExpression lambda) + { + if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ") + { + MemberExpression m = lambda.Body as MemberExpression; + + if (m.Expression != null) + { + string r = VisitMemberAccess(m); + + SqlParameters.Add(1); + return string.Format("{0}=@{1}", r, SqlParameters.Count - 1); + + //return string.Format("{0}={1}", r, GetQuotedTrueValue()); + } + + } + return Visit(lambda.Body); + } + + protected virtual string VisitBinary(BinaryExpression b) + { + string left, right; + var operand = BindOperant(b.NodeType); //sep= " " ?? + if (operand == "AND" || operand == "OR") + { + MemberExpression m = b.Left as MemberExpression; + if (m != null && m.Expression != null) + { + string r = VisitMemberAccess(m); + + SqlParameters.Add(1); + left = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); + + //left = string.Format("{0}={1}", r, GetQuotedTrueValue()); + } + else + { + left = Visit(b.Left); + } + m = b.Right as MemberExpression; + if (m != null && m.Expression != null) + { + string r = VisitMemberAccess(m); + + SqlParameters.Add(1); + right = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); + + //right = string.Format("{0}={1}", r, GetQuotedTrueValue()); + } + else + { + right = Visit(b.Right); + } + } + else + { + left = Visit(b.Left); + right = Visit(b.Right); + } + + if (operand == "=" && right == "null") operand = "is"; + else if (operand == "<>" && right == "null") operand = "is not"; + else if (operand == "=" || operand == "<>") + { + //if (IsTrueExpression(right)) right = GetQuotedTrueValue(); + //else if (IsFalseExpression(right)) right = GetQuotedFalseValue(); + + //if (IsTrueExpression(left)) left = GetQuotedTrueValue(); + //else if (IsFalseExpression(left)) left = GetQuotedFalseValue(); + + } + + switch (operand) + { + case "MOD": + case "COALESCE": + return string.Format("{0}({1},{2})", operand, left, right); + default: + return left + sep + operand + sep + right; + } + } + + protected virtual List VisitExpressionList(ReadOnlyCollection original) + { + var list = new List(); + for (int i = 0, n = original.Count; i < n; i++) + { + if (original[i].NodeType == ExpressionType.NewArrayInit || + original[i].NodeType == ExpressionType.NewArrayBounds) + { + + list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression)); + } + else + list.Add(Visit(original[i])); + + } + return list; + } + + protected virtual string VisitNew(NewExpression nex) + { + // TODO : check ! + var member = Expression.Convert(nex, typeof(object)); + var lambda = Expression.Lambda>(member); + try + { + var getter = lambda.Compile(); + object o = getter(); + + SqlParameters.Add(o); + return string.Format("(@{0})", SqlParameters.Count - 1); + + //return GetQuotedValue(o, o.GetType()); + } + catch (InvalidOperationException) + { // FieldName ? + List exprs = VisitExpressionList(nex.Arguments); + var r = new StringBuilder(); + foreach (Object e in exprs) + { + r.AppendFormat("{0}{1}", + r.Length > 0 ? "," : "", + e); + } + return r.ToString(); + } + + } + + protected virtual string VisitParameter(ParameterExpression p) + { + return p.Name; + } + + protected virtual string VisitConstant(ConstantExpression c) + { + if (c.Value == null) + return "null"; + + SqlParameters.Add(c.Value); + return string.Format("(@{0})", SqlParameters.Count - 1); + + //if (c.Value is bool) + //{ + // object o = GetQuotedValue(c.Value, c.Value.GetType()); + // return string.Format("({0}={1})", GetQuotedTrueValue(), o); + //} + //return GetQuotedValue(c.Value, c.Value.GetType()); + } + + protected virtual string VisitUnary(UnaryExpression u) + { + //switch (u.NodeType) + //{ + // case ExpressionType.Not: + // string o = Visit(u.Operand); + + // if (IsFieldName(o)) + // o = o + "=" + GetQuotedValue(true, typeof(bool)); + + // return "NOT (" + o + ")"; + // default: + // return Visit(u.Operand); + //} + + return Visit(u.Operand); + + } + + protected virtual string VisitNewArray(NewArrayExpression na) + { + + List exprs = VisitExpressionList(na.Expressions); + var r = new StringBuilder(); + foreach (Object e in exprs) + { + r.Append(r.Length > 0 ? "," + e : e); + } + + return r.ToString(); + } + + protected virtual List VisitNewArrayFromExpressionList(NewArrayExpression na) + { + + List exprs = VisitExpressionList(na.Expressions); + return exprs; + } + + protected virtual string BindOperant(ExpressionType e) + { + + switch (e) + { + case ExpressionType.Equal: + return "="; + case ExpressionType.NotEqual: + return "<>"; + case ExpressionType.GreaterThan: + return ">"; + case ExpressionType.GreaterThanOrEqual: + return ">="; + case ExpressionType.LessThan: + return "<"; + case ExpressionType.LessThanOrEqual: + return "<="; + case ExpressionType.AndAlso: + return "AND"; + case ExpressionType.OrElse: + return "OR"; + case ExpressionType.Add: + return "+"; + case ExpressionType.Subtract: + return "-"; + case ExpressionType.Multiply: + return "*"; + case ExpressionType.Divide: + return "/"; + case ExpressionType.Modulo: + return "MOD"; + case ExpressionType.Coalesce: + return "COALESCE"; + default: + return e.ToString(); + } + } + + protected virtual string VisitMethodCall(MethodCallExpression m) + { + List args = this.VisitExpressionList(m.Arguments); + + Object r; + if (m.Object != null) + r = Visit(m.Object); + else + { + r = args[0]; + args.RemoveAt(0); + } + + //TODO: We should probably add the same logic we've done for ModelToSqlExpressionHelper with checking for: + // InvariantStartsWith, InvariantEndsWith, SqlWildcard, etc... + // since we should be able to easily handle that with the Poco objects too. + + switch (m.Method.Name) + { + case "ToUpper": + return string.Format("upper({0})", r); + case "ToLower": + return string.Format("lower({0})", r); + case "SqlWildcard": + case "StartsWith": + case "EndsWith": + case "Contains": + case "Equals": + case "SqlStartsWith": + case "SqlEndsWith": + case "SqlContains": + case "SqlEquals": + case "InvariantStartsWith": + case "InvariantEndsWith": + case "InvariantContains": + case "InvariantEquals": + + //default + var colType = TextColumnType.NVarchar; + //then check if this arg has been passed in + if (m.Arguments.Count > 1) + { + //special case, if it is 'Contains' and the member that Contains is being called on is not a string, then + // we should be doing an 'In' clause - but we currently do not support this, nor is it very necessary + if (TypeHelper.IsTypeAssignableFrom(m.Arguments[0].Type)) + { + throw new NotSupportedException("An array Contains method is not supported"); + } + + var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType)); + if (colTypeArg != null) + { + colType = (TextColumnType)((ConstantExpression)colTypeArg).Value; + } + } + return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType); + case "Substring": + var startIndex = Int32.Parse(args[0].ToString()) + 1; + if (args.Count == 2) + { + var length = Int32.Parse(args[1].ToString()); + return string.Format("substring({0} from {1} for {2})", + r, + startIndex, + length); + } + else + return string.Format("substring({0} from {1})", + r, + startIndex); + //case "Round": + //case "Floor": + //case "Ceiling": + //case "Coalesce": + //case "Abs": + //case "Sum": + // return string.Format("{0}({1}{2})", + // m.Method.Name, + // r, + // args.Count == 1 ? string.Format(",{0}", args[0]) : ""); + //case "Concat": + // var s = new StringBuilder(); + // foreach (Object e in args) + // { + // s.AppendFormat(" || {0}", e); + // } + // return string.Format("{0}{1}", r, s); + + //case "In": + + // var member = Expression.Convert(m.Arguments[0], typeof(object)); + // var lambda = Expression.Lambda>(member); + // var getter = lambda.Compile(); + + // var inArgs = (object[])getter(); + + // var sIn = new StringBuilder(); + // foreach (var e in inArgs) + // { + // SqlParameters.Add(e); + + // sIn.AppendFormat("{0}{1}", + // sIn.Length > 0 ? "," : "", + // string.Format("@{0}", SqlParameters.Count - 1)); + + // //sIn.AppendFormat("{0}{1}", + // // sIn.Length > 0 ? "," : "", + // // GetQuotedValue(e, e.GetType())); + // } + + // return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString()); + //case "Desc": + // return string.Format("{0} DESC", r); + //case "Alias": + //case "As": + // return string.Format("{0} As {1}", r, + // GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString())))); + case "ToString": + return r.ToString(); + default: + + throw new ArgumentOutOfRangeException("No logic supported for " + m.Method.Name); + + //var s2 = new StringBuilder(); + //foreach (Object e in args) + //{ + // s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType())); + //} + //return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString()); + } + } + + public virtual string GetQuotedTableName(string tableName) + { + return string.Format("\"{0}\"", tableName); + } + + public virtual string GetQuotedColumnName(string columnName) + { + return string.Format("\"{0}\"", columnName); + } + + public virtual string GetQuotedName(string name) + { + return string.Format("\"{0}\"", name); + } + + //private string GetQuotedTrueValue() + //{ + // return GetQuotedValue(true, typeof(bool)); + //} + + //private string GetQuotedFalseValue() + //{ + // return GetQuotedValue(false, typeof(bool)); + //} + + //public virtual string GetQuotedValue(object value, Type fieldType) + //{ + // return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue); + //} + + //private string GetTrueExpression() + //{ + // object o = GetQuotedTrueValue(); + // return string.Format("({0}={1})", o, o); + //} + + //private string GetFalseExpression() + //{ + + // return string.Format("({0}={1})", + // GetQuotedTrueValue(), + // GetQuotedFalseValue()); + //} + + //private bool IsTrueExpression(string exp) + //{ + // return (exp == GetTrueExpression()); + //} + + //private bool IsFalseExpression(string exp) + //{ + // return (exp == GetFalseExpression()); + //} + } + /// /// Logic that is shared with the expression helpers /// @@ -22,15 +508,26 @@ namespace Umbraco.Core.Persistence.Querying switch (verb) { case "SqlWildcard": - return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, RemoveQuote(val), columnType); + SqlParameters.Add(RemoveQuote(val)); + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); case "Equals": - return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, RemoveQuote(val), columnType); + SqlParameters.Add(RemoveQuote(val)); + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, SqlParameters.Count - 1, columnType); case "StartsWith": - return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, RemoveQuote(val), columnType); + SqlParameters.Add(string.Format("{0}{1}", + RemoveQuote(val), + SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder())); + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); case "EndsWith": - return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, RemoveQuote(val), columnType); + SqlParameters.Add(string.Format("{0}{1}", + SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder(), + RemoveQuote(val))); + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); case "Contains": - return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, RemoveQuote(val), columnType); + SqlParameters.Add(string.Format("{0}{1}{0}", + SqlSyntaxContext.SqlSyntaxProvider.GetWildcardPlaceholder(), + RemoveQuote(val))); + return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnWildcardComparison(col, SqlParameters.Count - 1, columnType); case "InvariantEquals": case "SqlEquals": //recurse diff --git a/src/Umbraco.Core/Persistence/Querying/IQuery.cs b/src/Umbraco.Core/Persistence/Querying/IQuery.cs index 1d634b9d90..b484465540 100644 --- a/src/Umbraco.Core/Persistence/Querying/IQuery.cs +++ b/src/Umbraco.Core/Persistence/Querying/IQuery.cs @@ -1,10 +1,26 @@ using System; +using System.Collections.Generic; using System.Linq.Expressions; namespace Umbraco.Core.Persistence.Querying { + /// + /// Represents a query for building Linq translatable SQL queries + /// + /// public interface IQuery { + /// + /// Adds a where clause to the query + /// + /// + /// This instance so calls to this method are chainable IQuery Where(Expression> predicate); + + /// + /// Returns all translated where clauses and their sql parameters + /// + /// + IEnumerable> GetWhereClauses(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs index 2e2308d5d0..addb13df01 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionHelper.cs @@ -10,161 +10,21 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Querying { - internal class ModelToSqlExpressionHelper : BaseExpressionHelper + internal class ModelToSqlExpressionHelper : BaseExpressionHelper { - private string sep = " "; + private readonly BaseMapper _mapper; public ModelToSqlExpressionHelper() { _mapper = MappingResolver.Current.ResolveMapperByType(typeof(T)); } - - protected internal virtual string Visit(Expression exp) + + protected override string VisitMemberAccess(MemberExpression m) { - - if (exp == null) return string.Empty; - switch (exp.NodeType) - { - case ExpressionType.Lambda: - return VisitLambda(exp as LambdaExpression); - case ExpressionType.MemberAccess: - return VisitMemberAccess(exp as MemberExpression); - case ExpressionType.Constant: - return VisitConstant(exp as ConstantExpression); - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Divide: - case ExpressionType.Modulo: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.Equal: - case ExpressionType.NotEqual: - case ExpressionType.Coalesce: - case ExpressionType.ArrayIndex: - case ExpressionType.RightShift: - case ExpressionType.LeftShift: - case ExpressionType.ExclusiveOr: - return VisitBinary(exp as BinaryExpression); - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.Not: - case ExpressionType.Convert: - case ExpressionType.ConvertChecked: - case ExpressionType.ArrayLength: - case ExpressionType.Quote: - case ExpressionType.TypeAs: - return VisitUnary(exp as UnaryExpression); - case ExpressionType.Parameter: - return VisitParameter(exp as ParameterExpression); - case ExpressionType.Call: - return VisitMethodCall(exp as MethodCallExpression); - case ExpressionType.New: - return VisitNew(exp as NewExpression); - case ExpressionType.NewArrayInit: - case ExpressionType.NewArrayBounds: - return VisitNewArray(exp as NewArrayExpression); - default: - return exp.ToString(); - } - } - - protected virtual string VisitLambda(LambdaExpression lambda) - { - if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ") - { - MemberExpression m = lambda.Body as MemberExpression; - - if (m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - return string.Format("{0}=@{1}", r, SqlParameters.Count - 1); - - //return string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - - } - return Visit(lambda.Body); - } - - protected virtual string VisitBinary(BinaryExpression b) - { - string left, right; - var operand = BindOperant(b.NodeType); //sep= " " ?? - if (operand == "AND" || operand == "OR") - { - MemberExpression m = b.Left as MemberExpression; - if (m != null && m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - left = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); - - //left = string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - else - { - left = Visit(b.Left); - } - m = b.Right as MemberExpression; - if (m != null && m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - right = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); - - //right = string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - else - { - right = Visit(b.Right); - } - } - else - { - left = Visit(b.Left); - right = Visit(b.Right); - } - - if (operand == "=" && right == "null") operand = "is"; - else if (operand == "<>" && right == "null") operand = "is not"; - else if (operand == "=" || operand == "<>") - { - //if (IsTrueExpression(right)) right = GetQuotedTrueValue(); - //else if (IsFalseExpression(right)) right = GetQuotedFalseValue(); - - //if (IsTrueExpression(left)) left = GetQuotedTrueValue(); - //else if (IsFalseExpression(left)) left = GetQuotedFalseValue(); - - } - - switch (operand) - { - case "MOD": - case "COALESCE": - return string.Format("{0}({1},{2})", operand, left, right); - default: - return left + sep + operand + sep + right; - } - } - - protected virtual string VisitMemberAccess(MemberExpression m) - { - if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T)) + if (m.Expression != null && + m.Expression.NodeType == ExpressionType.Parameter + && m.Expression.Type == typeof(T)) { var field = _mapper.Map(m.Member.Name); return field; @@ -187,341 +47,7 @@ namespace Umbraco.Core.Persistence.Querying //return GetQuotedValue(o, o != null ? o.GetType() : null); } - - protected virtual string VisitNew(NewExpression nex) - { - // TODO : check ! - var member = Expression.Convert(nex, typeof(object)); - var lambda = Expression.Lambda>(member); - try - { - var getter = lambda.Compile(); - object o = getter(); - - SqlParameters.Add(o); - return string.Format("(@{0})", SqlParameters.Count - 1); - - //return GetQuotedValue(o, o.GetType()); - } - catch (System.InvalidOperationException) - { // FieldName ? - List exprs = VisitExpressionList(nex.Arguments); - var r = new StringBuilder(); - foreach (Object e in exprs) - { - r.AppendFormat("{0}{1}", r.Length > 0 ? "," : "", e); - } - return r.ToString(); - } - - } - - protected virtual string VisitParameter(ParameterExpression p) - { - return p.Name; - } - - protected virtual string VisitConstant(ConstantExpression c) - { - if (c.Value == null) - return "null"; - - SqlParameters.Add(c.Value); - return string.Format("(@{0})", SqlParameters.Count - 1); - - //if (c.Value is bool) - //{ - // object o = GetQuotedValue(c.Value, c.Value.GetType()); - // return string.Format("({0}={1})", GetQuotedTrueValue(), o); - //} - //return GetQuotedValue(c.Value, c.Value.GetType()); - } - - protected virtual string VisitUnary(UnaryExpression u) - { - //switch (u.NodeType) - //{ - // case ExpressionType.Not: - // string o = Visit(u.Operand); - // if (IsFieldName(o)) o = o + "=" + GetQuotedValue(true, typeof(bool)); - // return "NOT (" + o + ")"; - // default: - // return Visit(u.Operand); - //} - - return Visit(u.Operand); - } - - protected virtual string VisitMethodCall(MethodCallExpression m) - { - List args = this.VisitExpressionList(m.Arguments); - - Object r; - if (m.Object != null) - r = Visit(m.Object); - else - { - r = args[0]; - args.RemoveAt(0); - } - - switch (m.Method.Name) - { - case "ToUpper": - return string.Format("upper({0})", r); - case "ToLower": - return string.Format("lower({0})", r); - case "SqlWildcard": - case "StartsWith": - case "EndsWith": - case "Contains": - case "Equals": - case "SqlStartsWith": - case "SqlEndsWith": - case "SqlContains": - case "SqlEquals": - case "InvariantStartsWith": - case "InvariantEndsWith": - case "InvariantContains": - case "InvariantEquals": - //default - var colType = TextColumnType.NVarchar; - //then check if this arg has been passed in - if (m.Arguments.Count > 1) - { - var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType)); - if (colTypeArg != null) - { - colType = (TextColumnType) ((ConstantExpression) colTypeArg).Value; - } - } - return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType); - case "Substring": - var startIndex = Int32.Parse(args[0].ToString()) + 1; - if (args.Count == 2) - { - var length = Int32.Parse(args[1].ToString()); - return string.Format("substring({0} from {1} for {2})", - r, - startIndex, - length); - } - else - return string.Format("substring({0} from {1})", - r, - startIndex); - case "Round": - case "Floor": - case "Ceiling": - case "Coalesce": - case "Abs": - case "Sum": - return string.Format("{0}({1}{2})", - m.Method.Name, - r, - args.Count == 1 ? string.Format(",{0}", args[0]) : ""); - case "Concat": - var s = new StringBuilder(); - foreach (Object e in args) - { - s.AppendFormat(" || {0}", e); - } - return string.Format("{0}{1}", r, s.ToString()); - - case "In": - - var member = Expression.Convert(m.Arguments[1], typeof(object)); - var lambda = Expression.Lambda>(member); - var getter = lambda.Compile(); - - var inArgs = getter() as object[]; - - var sIn = new StringBuilder(); - foreach (Object e in inArgs) - { - if (e.GetType().ToString() != "System.Collections.Generic.List`1[System.Object]") - { - SqlParameters.Add(e); - - sIn.AppendFormat("{0}{1}", - sIn.Length > 0 ? "," : "", - string.Format("@{0}", SqlParameters.Count - 1)); - - //sIn.AppendFormat("{0}{1}", - // sIn.Length > 0 ? "," : "", - // GetQuotedValue(e, e.GetType())); - } - else - { - var listArgs = e as IList; - foreach (Object el in listArgs) - { - SqlParameters.Add(el); - - sIn.AppendFormat("{0}{1}", - sIn.Length > 0 ? "," : "", - string.Format("@{0}", SqlParameters.Count - 1)); - - //sIn.AppendFormat("{0}{1}", - // sIn.Length > 0 ? "," : "", - // GetQuotedValue(el, el.GetType())); - } - } - } - - return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString()); - case "Desc": - return string.Format("{0} DESC", r); - case "Alias": - case "As": - return string.Format("{0} As {1}", r, - GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString())))); - case "ToString": - return r.ToString(); - default: - - return r.ToString(); - - //var s2 = new StringBuilder(); - //foreach (Object e in args) - //{ - // s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType())); - //} - //return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString()); - } - } - - protected virtual List VisitExpressionList(ReadOnlyCollection original) - { - var list = new List(); - for (int i = 0, n = original.Count; i < n; i++) - { - if (original[i].NodeType == ExpressionType.NewArrayInit || - original[i].NodeType == ExpressionType.NewArrayBounds) - { - - list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression)); - } - else - list.Add(Visit(original[i])); - - } - return list; - } - - protected virtual string VisitNewArray(NewArrayExpression na) - { - - List exprs = VisitExpressionList(na.Expressions); - var r = new StringBuilder(); - foreach (Object e in exprs) - { - r.Append(r.Length > 0 ? "," + e : e); - } - - return r.ToString(); - } - - protected virtual List VisitNewArrayFromExpressionList(NewArrayExpression na) - { - - List exprs = VisitExpressionList(na.Expressions); - return exprs; - } - - - protected virtual string BindOperant(ExpressionType e) - { - - switch (e) - { - case ExpressionType.Equal: - return "="; - case ExpressionType.NotEqual: - return "<>"; - case ExpressionType.GreaterThan: - return ">"; - case ExpressionType.GreaterThanOrEqual: - return ">="; - case ExpressionType.LessThan: - return "<"; - case ExpressionType.LessThanOrEqual: - return "<="; - case ExpressionType.AndAlso: - return "AND"; - case ExpressionType.OrElse: - return "OR"; - case ExpressionType.Add: - return "+"; - case ExpressionType.Subtract: - return "-"; - case ExpressionType.Multiply: - return "*"; - case ExpressionType.Divide: - return "/"; - case ExpressionType.Modulo: - return "MOD"; - case ExpressionType.Coalesce: - return "COALESCE"; - default: - return e.ToString(); - } - } - - public virtual string GetQuotedTableName(string tableName) - { - return string.Format("\"{0}\"", tableName); - } - - public virtual string GetQuotedColumnName(string columnName) - { - return string.Format("\"{0}\"", columnName); - } - - public virtual string GetQuotedName(string name) - { - return string.Format("\"{0}\"", name); - } - - //private string GetQuotedTrueValue() - //{ - // return GetQuotedValue(true, typeof(bool)); - //} - - //private string GetQuotedFalseValue() - //{ - // return GetQuotedValue(false, typeof(bool)); - //} - - //public virtual string GetQuotedValue(object value, Type fieldType) - //{ - // return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue); - //} - - //private string GetTrueExpression() - //{ - // object o = GetQuotedTrueValue(); - // return string.Format("({0}={1})", o, o); - //} - - //private string GetFalseExpression() - //{ - - // return string.Format("({0}={1})", - // GetQuotedTrueValue(), - // GetQuotedFalseValue()); - //} - - //private bool IsTrueExpression(string exp) - //{ - // return (exp == GetTrueExpression()); - //} - - //private bool IsFalseExpression(string exp) - //{ - // return (exp == GetFalseExpression()); - //} - + protected bool IsFieldName(string quotedExp) { //Not entirely sure this is reliable, but its better then simply returning true diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs index 424ad62e28..b1ab50b7c3 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -8,171 +9,28 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Querying { - internal class PocoToSqlExpressionHelper : BaseExpressionHelper + internal class PocoToSqlExpressionHelper : BaseExpressionHelper { - private string sep = " "; - private Database.PocoData pd; + private readonly Database.PocoData _pd; public PocoToSqlExpressionHelper() { - pd = new Database.PocoData(typeof(T)); + _pd = new Database.PocoData(typeof(T)); } - - protected internal virtual string Visit(Expression exp) - { - - if (exp == null) return string.Empty; - switch (exp.NodeType) - { - case ExpressionType.Lambda: - return VisitLambda(exp as LambdaExpression); - case ExpressionType.MemberAccess: - return VisitMemberAccess(exp as MemberExpression); - case ExpressionType.Constant: - return VisitConstant(exp as ConstantExpression); - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.Divide: - case ExpressionType.Modulo: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.Equal: - case ExpressionType.NotEqual: - case ExpressionType.Coalesce: - case ExpressionType.ArrayIndex: - case ExpressionType.RightShift: - case ExpressionType.LeftShift: - case ExpressionType.ExclusiveOr: - return VisitBinary(exp as BinaryExpression); - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.Not: - case ExpressionType.Convert: - case ExpressionType.ConvertChecked: - case ExpressionType.ArrayLength: - case ExpressionType.Quote: - case ExpressionType.TypeAs: - return VisitUnary(exp as UnaryExpression); - case ExpressionType.Parameter: - return VisitParameter(exp as ParameterExpression); - case ExpressionType.Call: - return VisitMethodCall(exp as MethodCallExpression); - case ExpressionType.New: - return VisitNew(exp as NewExpression); - case ExpressionType.NewArrayInit: - case ExpressionType.NewArrayBounds: - return VisitNewArray(exp as NewArrayExpression); - default: - return exp.ToString(); - } - } - - protected virtual string VisitLambda(LambdaExpression lambda) - { - if (lambda.Body.NodeType == ExpressionType.MemberAccess && sep == " ") - { - MemberExpression m = lambda.Body as MemberExpression; - - if (m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - return string.Format("{0}=@{1}", r, 1); - - //return string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - - } - return Visit(lambda.Body); - } - - protected virtual string VisitBinary(BinaryExpression b) - { - string left, right; - var operand = BindOperant(b.NodeType); //sep= " " ?? - if (operand == "AND" || operand == "OR") - { - MemberExpression m = b.Left as MemberExpression; - if (m != null && m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - left = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); - - //left = string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - else - { - left = Visit(b.Left); - } - m = b.Right as MemberExpression; - if (m != null && m.Expression != null) - { - string r = VisitMemberAccess(m); - - SqlParameters.Add(1); - right = string.Format("{0}=@{1}", r, SqlParameters.Count - 1); - - //right = string.Format("{0}={1}", r, GetQuotedTrueValue()); - } - else - { - right = Visit(b.Right); - } - } - else - { - left = Visit(b.Left); - right = Visit(b.Right); - } - - if (operand == "=" && right == "null") operand = "is"; - else if (operand == "<>" && right == "null") operand = "is not"; - else if (operand == "=" || operand == "<>") - { - //if (IsTrueExpression(right)) right = GetQuotedTrueValue(); - //else if (IsFalseExpression(right)) right = GetQuotedFalseValue(); - - //if (IsTrueExpression(left)) left = GetQuotedTrueValue(); - //else if (IsFalseExpression(left)) left = GetQuotedFalseValue(); - - } - - switch (operand) - { - case "MOD": - case "COALESCE": - return string.Format("{0}({1},{2})", operand, left, right); - default: - return left + sep + operand + sep + right; - } - } - - protected virtual string VisitMemberAccess(MemberExpression m) + + protected override string VisitMemberAccess(MemberExpression m) { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == typeof(T)) { - string field = GetFieldName(pd, m.Member.Name); + string field = GetFieldName(_pd, m.Member.Name); return field; } if (m.Expression != null && m.Expression.NodeType == ExpressionType.Convert) { - string field = GetFieldName(pd, m.Member.Name); + string field = GetFieldName(_pd, m.Member.Name); return field; } @@ -187,330 +45,7 @@ namespace Umbraco.Core.Persistence.Querying //return GetQuotedValue(o, o != null ? o.GetType() : null); } - - protected virtual string VisitNew(NewExpression nex) - { - // TODO : check ! - var member = Expression.Convert(nex, typeof(object)); - var lambda = Expression.Lambda>(member); - try - { - var getter = lambda.Compile(); - object o = getter(); - - SqlParameters.Add(o); - return string.Format("(@{0})", SqlParameters.Count - 1); - - //return GetQuotedValue(o, o.GetType()); - } - catch (InvalidOperationException) - { // FieldName ? - List exprs = VisitExpressionList(nex.Arguments); - var r = new StringBuilder(); - foreach (Object e in exprs) - { - r.AppendFormat("{0}{1}", - r.Length > 0 ? "," : "", - e); - } - return r.ToString(); - } - - } - - protected virtual string VisitParameter(ParameterExpression p) - { - return p.Name; - } - - protected virtual string VisitConstant(ConstantExpression c) - { - if (c.Value == null) - return "null"; - - SqlParameters.Add(c.Value); - return string.Format("(@{0})", SqlParameters.Count - 1); - - //if (c.Value is bool) - //{ - // object o = GetQuotedValue(c.Value, c.Value.GetType()); - - // SqlParameters.Add(o); - - // return string.Format("({0}=@{1})", GetQuotedTrueValue(), SqlParameters.Count); - //} - //return GetQuotedValue(c.Value, c.Value.GetType()); - } - - protected virtual string VisitUnary(UnaryExpression u) - { - //switch (u.NodeType) - //{ - // case ExpressionType.Not: - // string o = Visit(u.Operand); - - // if (IsFieldName(o)) - // o = o + "=" + GetQuotedValue(true, typeof(bool)); - - // return "NOT (" + o + ")"; - // default: - // return Visit(u.Operand); - //} - - return Visit(u.Operand); - - } - - protected virtual string VisitMethodCall(MethodCallExpression m) - { - List args = this.VisitExpressionList(m.Arguments); - - Object r; - if (m.Object != null) - r = Visit(m.Object); - else - { - r = args[0]; - args.RemoveAt(0); - } - - //TODO: We should probably add the same logic we've done for ModelToSqlExpressionHelper with checking for: - // InvariantStartsWith, InvariantEndsWith, SqlWildcard, etc... - // since we should be able to easily handle that with the Poco objects too. - - switch (m.Method.Name) - { - case "ToUpper": - return string.Format("upper({0})", r); - case "ToLower": - return string.Format("lower({0})", r); - case "SqlWildcard": - case "StartsWith": - case "EndsWith": - case "Contains": - case "Equals": - case "SqlStartsWith": - case "SqlEndsWith": - case "SqlContains": - case "SqlEquals": - case "InvariantStartsWith": - case "InvariantEndsWith": - case "InvariantContains": - case "InvariantEquals": - //default - var colType = TextColumnType.NVarchar; - //then check if this arg has been passed in - if (m.Arguments.Count > 1) - { - var colTypeArg = m.Arguments.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType)); - if (colTypeArg != null) - { - colType = (TextColumnType)((ConstantExpression)colTypeArg).Value; - } - } - return HandleStringComparison(r.ToString(), args[0].ToString(), m.Method.Name, colType); - case "Substring": - var startIndex = Int32.Parse(args[0].ToString()) + 1; - if (args.Count == 2) - { - var length = Int32.Parse(args[1].ToString()); - return string.Format("substring({0} from {1} for {2})", - r, - startIndex, - length); - } - else - return string.Format("substring({0} from {1})", - r, - startIndex); - case "Round": - case "Floor": - case "Ceiling": - case "Coalesce": - case "Abs": - case "Sum": - return string.Format("{0}({1}{2})", - m.Method.Name, - r, - args.Count == 1 ? string.Format(",{0}", args[0]) : ""); - case "Concat": - var s = new StringBuilder(); - foreach (Object e in args) - { - s.AppendFormat(" || {0}", e); - } - return string.Format("{0}{1}", r, s.ToString()); - - case "In": - - var member = Expression.Convert(m.Arguments[1], typeof(object)); - var lambda = Expression.Lambda>(member); - var getter = lambda.Compile(); - - var inArgs = getter() as object[]; - - var sIn = new StringBuilder(); - foreach (Object e in inArgs) - { - if (e.GetType().ToString() != "System.Collections.Generic.List`1[System.Object]") - { - SqlParameters.Add(e); - - sIn.AppendFormat("{0}{1}", - sIn.Length > 0 ? "," : "", - string.Format("@{0}", SqlParameters.Count - 1)); - - //sIn.AppendFormat("{0}{1}", - // sIn.Length > 0 ? "," : "", - // GetQuotedValue(e, e.GetType())); - } - else - { - var listArgs = e as IList; - foreach (Object el in listArgs) - { - SqlParameters.Add(el); - - sIn.AppendFormat("{0}{1}", - sIn.Length > 0 ? "," : "", - string.Format("@{0}", SqlParameters.Count - 1)); - - //sIn.AppendFormat("{0}{1}", - // sIn.Length > 0 ? "," : "", - // GetQuotedValue(el, el.GetType())); - } - } - } - - return string.Format("{0} {1} ({2})", r, m.Method.Name, sIn.ToString()); - case "Desc": - return string.Format("{0} DESC", r); - case "Alias": - case "As": - return string.Format("{0} As {1}", r, - GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString())))); - case "ToString": - return r.ToString(); - default: - - return r.ToString(); - - //var s2 = new StringBuilder(); - //foreach (Object e in args) - //{ - // s2.AppendFormat(",{0}", GetQuotedValue(e, e.GetType())); - //} - //return string.Format("{0}({1}{2})", m.Method.Name, r, s2.ToString()); - } - } - - protected virtual List VisitExpressionList(ReadOnlyCollection original) - { - var list = new List(); - for (int i = 0, n = original.Count; i < n; i++) - { - if (original[i].NodeType == ExpressionType.NewArrayInit || - original[i].NodeType == ExpressionType.NewArrayBounds) - { - - list.AddRange(VisitNewArrayFromExpressionList(original[i] as NewArrayExpression)); - } - else - list.Add(Visit(original[i])); - - } - return list; - } - - protected virtual string VisitNewArray(NewArrayExpression na) - { - - List exprs = VisitExpressionList(na.Expressions); - var r = new StringBuilder(); - foreach (Object e in exprs) - { - r.Append(r.Length > 0 ? "," + e : e); - } - - return r.ToString(); - } - - protected virtual List VisitNewArrayFromExpressionList(NewArrayExpression na) - { - - List exprs = VisitExpressionList(na.Expressions); - return exprs; - } - - - protected virtual string BindOperant(ExpressionType e) - { - - switch (e) - { - case ExpressionType.Equal: - return "="; - case ExpressionType.NotEqual: - return "<>"; - case ExpressionType.GreaterThan: - return ">"; - case ExpressionType.GreaterThanOrEqual: - return ">="; - case ExpressionType.LessThan: - return "<"; - case ExpressionType.LessThanOrEqual: - return "<="; - case ExpressionType.AndAlso: - return "AND"; - case ExpressionType.OrElse: - return "OR"; - case ExpressionType.Add: - return "+"; - case ExpressionType.Subtract: - return "-"; - case ExpressionType.Multiply: - return "*"; - case ExpressionType.Divide: - return "/"; - case ExpressionType.Modulo: - return "MOD"; - case ExpressionType.Coalesce: - return "COALESCE"; - default: - return e.ToString(); - } - } - - public virtual string GetQuotedTableName(string tableName) - { - return string.Format("\"{0}\"", tableName); - } - - public virtual string GetQuotedColumnName(string columnName) - { - return string.Format("\"{0}\"", columnName); - } - - public virtual string GetQuotedName(string name) - { - return string.Format("\"{0}\"", name); - } - - //private string GetQuotedTrueValue() - //{ - // return GetQuotedValue(true, typeof(bool)); - //} - - //private string GetQuotedFalseValue() - //{ - // return GetQuotedValue(false, typeof(bool)); - //} - - //public virtual string GetQuotedValue(object value, Type fieldType) - //{ - // return GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue); - //} - + protected virtual string GetFieldName(Database.PocoData pocoData, string name) { var column = pocoData.Columns.FirstOrDefault(x => x.Value.PropertyInfo.Name == name); @@ -519,29 +54,7 @@ namespace Umbraco.Core.Persistence.Querying SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(column.Value.ColumnName)); } - //private string GetTrueExpression() - //{ - // object o = GetQuotedTrueValue(); - // return string.Format("({0}={1})", o, o); - //} - - //private string GetFalseExpression() - //{ - - // return string.Format("({0}={1})", - // GetQuotedTrueValue(), - // GetQuotedFalseValue()); - //} - - //private bool IsTrueExpression(string exp) - //{ - // return (exp == GetTrueExpression()); - //} - - //private bool IsFalseExpression(string exp) - //{ - // return (exp == GetFalseExpression()); - //} + protected bool IsFieldName(string quotedExp) { diff --git a/src/Umbraco.Core/Persistence/Querying/Query.cs b/src/Umbraco.Core/Persistence/Querying/Query.cs index b426619dcf..4dd268268f 100644 --- a/src/Umbraco.Core/Persistence/Querying/Query.cs +++ b/src/Umbraco.Core/Persistence/Querying/Query.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; namespace Umbraco.Core.Persistence.Querying @@ -10,37 +11,46 @@ namespace Umbraco.Core.Persistence.Querying /// public class Query : IQuery { - //private readonly ExpressionHelper _expresionist = new ExpressionHelper(); - private readonly ModelToSqlExpressionHelper _expresionist = new ModelToSqlExpressionHelper(); - private readonly List _wheres = new List(); - - public Query() - : base() - { - - } + private readonly List> _wheres = new List>(); + /// + /// Helper method to be used instead of manually creating an instance + /// public static IQuery Builder { - get - { - return new Query(); - } + get { return new Query(); } } + /// + /// Adds a where clause to the query + /// + /// + /// This instance so calls to this method are chainable public virtual IQuery Where(Expression> predicate) { if (predicate != null) { - string whereExpression = _expresionist.Visit(predicate); - _wheres.Add(whereExpression); + var expressionHelper = new ModelToSqlExpressionHelper(); + string whereExpression = expressionHelper.Visit(predicate); + + _wheres.Add(new Tuple(whereExpression, expressionHelper.GetSqlParameters())); } return this; } - - public List WhereClauses() + + /// + /// Returns all translated where clauses and their sql parameters + /// + /// + public IEnumerable> GetWhereClauses() { return _wheres; } + + [Obsolete("This is no longer used, use the GetWhereClauses method which includes the SQL parameters")] + public List WhereClauses() + { + return _wheres.Select(x => x.Item1).ToList(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs b/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs index ef5e14a019..b012293340 100644 --- a/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs +++ b/src/Umbraco.Core/Persistence/Querying/SqlTranslator.cs @@ -15,14 +15,10 @@ namespace Umbraco.Core.Persistence.Querying if (sql == null) throw new Exception("Sql cannot be null"); - var query1 = query as Query; - if (query1 == null) - throw new Exception("Query cannot be null"); - _sql = sql; - foreach (var clause in query1.WhereClauses()) + foreach (var clause in query.GetWhereClauses()) { - _sql.Where(clause); + _sql.Where(clause.Item1, clause.Item2); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index c4417b71b4..2820e76200 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -193,7 +193,10 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetByQuery(IQuery query) { - var wheres = string.Join(" AND ", ((Query) query).WhereClauses()); + //TODO: We need to fix all of this and how it handles parameters! + + var wheres = string.Join(" AND ", query.GetWhereClauses()); + var sqlClause = GetBase(false, false, sql1 => sql1.Where(wheres)); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(false, false)); @@ -208,10 +211,12 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetByQuery(IQuery query, Guid objectTypeId) { + //TODO: We need to fix all of this and how it handles parameters! + bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); - var wheres = string.Join(" AND ", ((Query)query).WhereClauses()); + var wheres = string.Join(" AND ", query.GetWhereClauses()); var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, sql1 => sql1.Where(wheres), objectTypeId); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 49dca038af..5c04cbcb46 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -91,14 +91,11 @@ namespace Umbraco.Core.Persistence.Repositories var baseQuery = GetBaseQuery(false); //check if the query is based on properties or not - var query1 = query as Query; - if (query1 == null) - throw new Exception("Query cannot be null"); - var wheres = query1.WhereClauses(); + var wheres = query.GetWhereClauses(); //this is a pretty rudimentary check but wil work, we just need to know if this query requires property // level queries - if (wheres.Any(x => x.Contains("cmsPropertyType"))) + if (wheres.Any(x => x.Item1.Contains("cmsPropertyType"))) { var sqlWithProps = GetNodeIdQueryWithPropertyData(); var translator = new SqlTranslator(sqlWithProps, query); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index b4c0020305..899a462172 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -13,10 +13,19 @@ namespace Umbraco.Core.Persistence.SqlSyntax { string EscapeString(string val); + string GetWildcardPlaceholder(); + string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType); + string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType); + + [Obsolete("Use the overload with the parameter index instead")] string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType); + [Obsolete("Use the overload with the parameter index instead")] string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType); + [Obsolete("Use the overload with the parameter index instead")] string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType); + [Obsolete("Use the overload with the parameter index instead")] string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType); + [Obsolete("Use the overload with the parameter index instead")] string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType); string GetQuotedTableName(string tableName); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs new file mode 100644 index 0000000000..84c6e6e824 --- /dev/null +++ b/src/Umbraco.Core/Persistence/SqlSyntax/MicrosoftSqlSyntaxProviderBase.cs @@ -0,0 +1,120 @@ +using System; +using Umbraco.Core.Persistence.Querying; + +namespace Umbraco.Core.Persistence.SqlSyntax +{ + /// + /// Abstract class for defining MS sql implementations + /// + /// + public abstract class MicrosoftSqlSyntaxProviderBase : SqlSyntaxProviderBase + where TSyntax : ISqlSyntaxProvider + { + public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } } + + public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } } + + public override string GetQuotedTableName(string tableName) + { + return string.Format("[{0}]", tableName); + } + + public override string GetQuotedColumnName(string columnName) + { + return string.Format("[{0}]", columnName); + } + + public override string GetQuotedName(string name) + { + return string.Format("[{0}]", name); + } + + public override string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEqualComparison(column, paramIndex, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for = comparison with NText columns but allows this syntax + return string.Format("{0} LIKE @{1}", column, paramIndex); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + public override string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnWildcardComparison(column, paramIndex, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE @{1}", column, paramIndex); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + [Obsolete("Use the overload with the parameter index instead")] + public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnStartsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + [Obsolete("Use the overload with the parameter index instead")] + public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnEndsWithComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + [Obsolete("Use the overload with the parameter index instead")] + public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '%{1}%'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + + [Obsolete("Use the overload with the parameter index instead")] + public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) + { + switch (columnType) + { + case TextColumnType.NVarchar: + return base.GetStringColumnContainsComparison(column, value, columnType); + case TextColumnType.NText: + //MSSQL doesn't allow for upper methods with NText columns + return string.Format("{0} LIKE '{1}'", column, value); + default: + throw new ArgumentOutOfRangeException("columnType"); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 170b10cb5e..44b8a761c9 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// Represents an SqlSyntaxProvider for Sql Ce /// [SqlSyntaxProviderAttribute("System.Data.SqlServerCe.4.0")] - public class SqlCeSyntaxProvider : SqlSyntaxProviderBase + public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase { public SqlCeSyntaxProvider() { @@ -60,6 +60,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax return indexType; } + [Obsolete("Use the overload with the parameter index instead")] public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) { switch (columnType) @@ -74,76 +75,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax } } - public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnStartsWithComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '{1}%'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnEndsWithComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '%{1}'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnContainsComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '%{1}%'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnContainsComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '{1}'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetQuotedTableName(string tableName) - { - return string.Format("[{0}]", tableName); - } - - public override string GetQuotedColumnName(string columnName) - { - return string.Format("[{0}]", columnName); - } - - public override string GetQuotedName(string name) - { - return string.Format("[{0}]", name); - } + public override string FormatColumnRename(string tableName, string oldName, string newName) { @@ -285,10 +217,10 @@ ORDER BY TABLE_NAME, INDEX_NAME"); } } - public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } } + public override string DropIndex { get { return "DROP INDEX {1}.{0}"; } } - public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index ca9e88fb1e..3388b984ee 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.SqlSyntax { @@ -10,7 +9,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// Represents an SqlSyntaxProvider for Sql Server /// [SqlSyntaxProviderAttribute("System.Data.SqlClient")] - public class SqlServerSyntaxProvider : SqlSyntaxProviderBase + public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase { public SqlServerSyntaxProvider() { @@ -33,91 +32,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// Gets/sets the version of the current SQL server instance /// internal Lazy VersionName { get; set; } - - public override string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnEqualComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for = comparison with NText columns but allows this syntax - return string.Format("{0} LIKE '{1}'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnStartsWithComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '{1}%'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnEndsWithComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '%{1}'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnContainsComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '%{1}%'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) - { - switch (columnType) - { - case TextColumnType.NVarchar: - return base.GetStringColumnContainsComparison(column, value, columnType); - case TextColumnType.NText: - //MSSQL doesn't allow for upper methods with NText columns - return string.Format("{0} LIKE '{1}'", column, value); - default: - throw new ArgumentOutOfRangeException("columnType"); - } - } - - public override string GetQuotedTableName(string tableName) - { - return string.Format("[{0}]", tableName); - } - - public override string GetQuotedColumnName(string columnName) - { - return string.Format("[{0}]", columnName); - } - - public override string GetQuotedName(string name) - { - return string.Format("[{0}]", name); - } + + public override IEnumerable GetTablesInSchema(Database db) { @@ -218,12 +134,11 @@ order by T.name, I.name"); get { return "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]"; } } - public override string AddColumn { get { return "ALTER TABLE {0} ADD {1}"; } } - + public override string DropIndex { get { return "DROP INDEX {0} ON {1}"; } } public override string RenameColumn { get { return "sp_rename '{0}.{1}', '{2}', 'COLUMN'"; } } - public override string RenameTable { get { return "sp_rename '{0}', '{1}'"; } } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 1ceeddafb7..f4935bf675 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -33,6 +33,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax }; } + public string GetWildcardPlaceholder() + { + return "%"; + } + public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})"; public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})"; @@ -108,30 +113,47 @@ namespace Umbraco.Core.Persistence.SqlSyntax return PetaPocoExtensions.EscapeAtSymbols(val.Replace("'", "''")); } + public virtual string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) = upper(@{1})", column, paramIndex); + } + + public virtual string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType) + { + //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. + return string.Format("upper({0}) like upper(@{1})", column, paramIndex); + } + + [Obsolete("Use the overload with the parameter index instead")] public virtual string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType) { //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. return string.Format("upper({0}) = '{1}'", column, value.ToUpper()); } + [Obsolete("Use the overload with the parameter index instead")] public virtual string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType) { //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. return string.Format("upper({0}) like '{1}%'", column, value.ToUpper()); } + [Obsolete("Use the overload with the parameter index instead")] public virtual string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType) { //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. return string.Format("upper({0}) like '%{1}'", column, value.ToUpper()); } + [Obsolete("Use the overload with the parameter index instead")] public virtual string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType) { //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. return string.Format("upper({0}) like '%{1}%'", column, value.ToUpper()); } + [Obsolete("Use the overload with the parameter index instead")] public virtual string GetStringColumnWildcardComparison(string column, string value, TextColumnType columnType) { //use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 14f4ef5aaa..cb8b975731 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -396,6 +396,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/PetaPocoDynamicQueryTests.cs b/src/Umbraco.Tests/Persistence/PetaPocoDynamicQueryTests.cs new file mode 100644 index 0000000000..eb177981c3 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/PetaPocoDynamicQueryTests.cs @@ -0,0 +1,133 @@ +//using System; +//using System.Linq; +//using NUnit.Framework; +//using Umbraco.Core.Models; +//using Umbraco.Core.Persistence; +//using Umbraco.Core.Persistence.SqlSyntax; +//using Umbraco.Tests.TestHelpers; +//using Umbraco.Tests.TestHelpers.Entities; + +//namespace Umbraco.Tests.Persistence +//{ +// [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] +// [TestFixture] +// public class PetaPocoDynamicQueryTests : BaseDatabaseFactoryTest +// { +// [Test] +// public void Check_Poco_Storage_Growth() +// { +// //CreateStuff(); + +// for (int i = 0; i < 1000; i++) +// { +// DatabaseContext.Database.Fetch( +// "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='" + i + "'"); +// } + +// //var oc11 = Database.PocoData.GetCachedPocoData().Count(); +// //var oc12 = Database.PocoData.GetConverters().Count(); +// //var oc13 = Database.GetAutoMappers().Count(); +// //var oc14 = Database.GetMultiPocoFactories().Count(); + +// //for (int i = 0; i < 2; i++) +// //{ +// // i1 = DatabaseContext.Database.Fetch("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES"); +// // r1 = i1.Select(x => x.TABLE_NAME).Cast().ToList(); +// //} + +// //var oc21 = Database.PocoData.GetCachedPocoData().Count(); +// //var oc22 = Database.PocoData.GetConverters().Count(); +// //var oc23 = Database.GetAutoMappers().Count(); +// //var oc24 = Database.GetMultiPocoFactories().Count(); + +// //var roots = ServiceContext.ContentService.GetRootContent(); +// //foreach (var content in roots) +// //{ +// // var d = ServiceContext.ContentService.GetDescendants(content); +// //} + +// //var oc31 = Database.PocoData.GetCachedPocoData().Count(); +// //var oc32 = Database.PocoData.GetConverters().Count(); +// //var oc33 = Database.GetAutoMappers().Count(); +// //var oc34 = Database.GetMultiPocoFactories().Count(); + +// //for (int i = 0; i < 2; i++) +// //{ +// // roots = ServiceContext.ContentService.GetRootContent(); +// // foreach (var content in roots) +// // { +// // var d = ServiceContext.ContentService.GetDescendants(content); +// // } +// //} + +// //var oc41 = Database.PocoData.GetCachedPocoData().Count(); +// //var oc42 = Database.PocoData.GetConverters().Count(); +// //var oc43 = Database.GetAutoMappers().Count(); +// //var oc44 = Database.GetMultiPocoFactories().Count(); + +// //var i2 = DatabaseContext.Database.Fetch("SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS"); +// //var r2 = +// // i2.Select( +// // item => +// // new ColumnInfo(item.TABLE_NAME, item.COLUMN_NAME, item.ORDINAL_POSITION, item.COLUMN_DEFAULT, +// // item.IS_NULLABLE, item.DATA_TYPE)).ToList(); + + +// var pocoData = Database.PocoData.GetCachedPocoData(); +// Console.WriteLine("GetCachedPocoData: " + pocoData.Count()); +// foreach (var keyValuePair in pocoData) +// { +// Console.WriteLine(keyValuePair.Value.GetFactories().Count()); +// } + +// Console.WriteLine("GetConverters: " + Database.PocoData.GetConverters().Count()); +// Console.WriteLine("GetAutoMappers: " + Database.GetAutoMappers().Count()); +// Console.WriteLine("GetMultiPocoFactories: " + Database.GetMultiPocoFactories().Count()); + +// //Assert.AreEqual(oc11, oc21); +// //Assert.AreEqual(oc12, oc22); +// //Assert.AreEqual(oc13, oc23); +// //Assert.AreEqual(oc14, oc24); + +// //Assert.AreEqual(oc31, oc41); +// //Assert.AreEqual(oc32, oc42); +// //Assert.AreEqual(oc33, oc43); +// //Assert.AreEqual(oc34, oc44); +// } + +// public void CreateStuff() +// { +// var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "test1"); +// var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "test2"); +// var contentType3 = MockedContentTypes.CreateTextpageContentType("test3", "test3"); +// ServiceContext.ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 }); +// contentType1.AllowedContentTypes = new[] +// { +// new ContentTypeSort(new Lazy(() => contentType2.Id), 0, contentType2.Alias), +// new ContentTypeSort(new Lazy(() => contentType3.Id), 1, contentType3.Alias) +// }; +// contentType2.AllowedContentTypes = new[] +// { +// new ContentTypeSort(new Lazy(() => contentType1.Id), 0, contentType1.Alias), +// new ContentTypeSort(new Lazy(() => contentType3.Id), 1, contentType3.Alias) +// }; +// contentType3.AllowedContentTypes = new[] +// { +// new ContentTypeSort(new Lazy(() => contentType1.Id), 0, contentType1.Alias), +// new ContentTypeSort(new Lazy(() => contentType2.Id), 1, contentType2.Alias) +// }; +// ServiceContext.ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 }); + +// var roots = MockedContent.CreateTextpageContent(contentType1, -1, 3); +// ServiceContext.ContentService.Save(roots); +// foreach (var root in roots) +// { +// var item1 = MockedContent.CreateTextpageContent(contentType1, root.Id, 3); +// var item2 = MockedContent.CreateTextpageContent(contentType2, root.Id, 3); +// var item3 = MockedContent.CreateTextpageContent(contentType3, root.Id, 3); + +// ServiceContext.ContentService.Save(item1.Concat(item2).Concat(item3)); +// } +// } +// } +//} \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs index 55531d112a..2b16b9e1d9 100644 --- a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs +++ b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -13,6 +14,7 @@ namespace Umbraco.Tests.Persistence.Querying public class PetaPocoSqlTests : BaseUsingSqlCeSyntax { + [Test] public void Generates_Sql_Parameter_Where_Clause_Single_Constant() { @@ -31,7 +33,18 @@ namespace Umbraco.Tests.Persistence.Querying Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[id] <> (@0) AND [umbracoNode].[id] <> (@1))", sql.SQL.Replace("\n", " ")); Assert.AreEqual(2, sql.Arguments.Length); Assert.AreEqual(2, sql.Arguments[0]); - Assert.AreEqual(3, sql.Arguments[0]); + Assert.AreEqual(3, sql.Arguments[1]); + } + + [Test] + public void Generates_Sql_Parameter_Where_Clause_Or_Constant() + { + var sql = new Sql("SELECT *").From().Where(x => x.Text == "hello" || x.NodeId == 3); + + Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[text] = (@0) OR [umbracoNode].[id] = (@1))", sql.SQL.Replace("\n", " ")); + Assert.AreEqual(2, sql.Arguments.Length); + Assert.AreEqual("hello", sql.Arguments[0]); + Assert.AreEqual(3, sql.Arguments[1]); } [Test] @@ -99,7 +112,7 @@ namespace Umbraco.Tests.Persistence.Querying public void Can_Use_Where_Predicate() { var expected = new Sql(); - expected.Select("*").From("[cmsContent]").Where("[cmsContent].[nodeId] = 1045"); + expected.Select("*").From("[cmsContent]").Where("[cmsContent].[nodeId] = (@0)", 1045); var sql = new Sql(); sql.Select("*").From().Where(x => x.NodeId == 1045); @@ -115,8 +128,8 @@ namespace Umbraco.Tests.Persistence.Querying var expected = new Sql(); expected.Select("*") .From("[cmsContent]") - .Where("[cmsContent].[nodeId] = 1045") - .Where("[cmsContent].[contentType] = 1050"); + .Where("[cmsContent].[nodeId] = (@0)", 1045) + .Where("[cmsContent].[contentType] = (@0)", 1050); var sql = new Sql(); sql.Select("*")