Adds ability to use the query builder with string matches based on an NText column

This commit is contained in:
Shannon
2013-12-18 17:22:00 +11:00
parent ef4246478d
commit a21aa079ff
16 changed files with 307 additions and 53 deletions

View File

@@ -21,14 +21,18 @@ namespace Umbraco.Core.Persistence
/// <summary>
/// This will escape single @ symbols for peta poco values so it doesn't think it's a parameter
/// </summary>
/// <param name="db"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string EscapeAtSymbols(this Database db, string value)
public static string EscapeAtSymbols(string value)
{
//this fancy regex will only match a single @ not a double, etc...
var regex = new Regex("(?<!@)@(?!@)");
return regex.Replace(value, "@@");
if (value.Contains("@"))
{
//this fancy regex will only match a single @ not a double, etc...
var regex = new Regex("(?<!@)@(?!@)");
return regex.Replace(value, "@@");
}
return value;
}
public static void CreateTable<T>(this Database db)

View File

@@ -64,13 +64,7 @@ namespace Umbraco.Core.Persistence.Querying
public virtual string EscapeAtArgument(string exp)
{
/*if (exp.StartsWith("@"))
return string.Concat("@", exp);*/
if (exp.Contains("@"))
return exp.Replace("@", "@@");
return exp;
return PetaPocoExtensions.EscapeAtSymbols(exp);
}
public virtual bool ShouldQuoteValue(Type fieldType)

View File

@@ -2,9 +2,11 @@
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.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
@@ -226,6 +228,39 @@ namespace Umbraco.Core.Persistence.Querying
}
private string HandleStringComparison(string col, string val, string verb, TextColumnType columnType)
{
switch (verb)
{
case "Equals":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEqualComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "StartsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnStartsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "EndsWith":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnEndsWithComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "Contains":
return SqlSyntaxContext.SqlSyntaxProvider.GetStringColumnContainsComparison(col, EscapeAtArgument(RemoveQuote(val)), columnType);
case "InvariantEquals":
case "SqlEquals":
//recurse
return HandleStringComparison(col, val, "Equals", columnType);
case "InvariantStartsWith":
case "SqlStartsWith":
//recurse
return HandleStringComparison(col, val, "StartsWith", columnType);
case "InvariantEndsWith":
case "SqlEndsWith":
//recurse
return HandleStringComparison(col, val, "EndsWith", columnType);
case "InvariantContains":
case "SqlContains":
//recurse
return HandleStringComparison(col, val, "Contains", columnType);
default:
throw new ArgumentOutOfRangeException("verb");
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> args = this.VisitExpressionList(m.Arguments);
@@ -245,12 +280,31 @@ namespace Umbraco.Core.Persistence.Querying
return string.Format("upper({0})", r);
case "ToLower":
return string.Format("lower({0})", r);
case "StartsWith":
return string.Format("upper({0}) like '{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString().ToUpper())));
case "EndsWith":
return string.Format("upper({0}) like '%{1}'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
case "Contains":
return string.Format("{0} like '%{1}%'", r, EscapeAtArgument(RemoveQuote(args[0].ToString()).ToUpper()));
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)

View File

@@ -0,0 +1,30 @@
using System;
namespace Umbraco.Core.Persistence.Querying
{
/// <summary>
/// String extension methods used specifically to translate into SQL
/// </summary>
internal static class SqlStringExtensions
{
public static bool SqlContains(this string str, string txt, TextColumnType columnType)
{
return str.InvariantContains(txt);
}
public static bool SqlEquals(this string str, string txt, TextColumnType columnType)
{
return str.InvariantEquals(txt);
}
public static bool SqlStartsWith(this string str, string txt, TextColumnType columnType)
{
return str.InvariantStartsWith(txt);
}
public static bool SqlEndsWith(this string str, string txt, TextColumnType columnType)
{
return str.InvariantEndsWith(txt);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Umbraco.Core.Persistence.Querying
{
public enum TextColumnType
{
NVarchar,
NText
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -10,6 +11,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// </summary>
public interface ISqlSyntaxProvider
{
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
string GetStringColumnStartsWithComparison(string column, string value, TextColumnType columnType);
string GetStringColumnEndsWithComparison(string column, string value, TextColumnType columnType);
string GetStringColumnContainsComparison(string column, string value, TextColumnType columnType);
string GetQuotedTableName(string tableName);
string GetQuotedColumnName(string columnName);
string GetQuotedName(string name);

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the MySql SqlSyntax Provider
/// </summary>
internal static class MySqlSyntax
{
public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } }
}
}

View File

@@ -6,14 +6,6 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the MySql SqlSyntax Provider
/// </summary>
internal static class MySqlSyntax
{
public static ISqlSyntaxProvider Provider { get { return new MySqlSyntaxProvider(); } }
}
/// <summary>
/// Represents an SqlSyntaxProvider for MySql
/// </summary>

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql CE SqlSyntax Provider
/// </summary>
internal static class SqlCeSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } }
}
}

View File

@@ -3,17 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql CE SqlSyntax Provider
/// </summary>
internal static class SqlCeSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlCeSyntaxProvider(); } }
}
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
@@ -67,6 +60,62 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return indexType;
}
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 GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql Server SqlSyntax Provider
/// </summary>
internal static class SqlServerSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } }
}
}

View File

@@ -2,31 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Static class that provides simple access to the Sql Server SqlSyntax Provider
/// </summary>
internal static class SqlServerSyntax
{
public static ISqlSyntaxProvider Provider { get { return new SqlServerSyntaxProvider(); } }
}
/// <summary>
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
/// </summary>
internal enum SqlServerVersionName
{
Invalid = -1,
V7 = 0,
V2000 = 1,
V2005 = 2,
V2008 = 3,
V2012 = 4,
Other = 5
}
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Server
/// </summary>
@@ -55,6 +34,62 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// </summary>
internal Lazy<SqlServerVersionName> 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 GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);

View File

@@ -0,0 +1,16 @@
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
/// </summary>
internal enum SqlServerVersionName
{
Invalid = -1,
V7 = 0,
V2000 = 1,
V2005 = 2,
V2008 = 3,
V2012 = 4,
Other = 5
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -102,6 +103,30 @@ namespace Umbraco.Core.Persistence.SqlSyntax
DbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
}
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());
}
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());
}
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());
}
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());
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);

View File

@@ -465,6 +465,11 @@ namespace Umbraco.Core
return compare.StartsWith(compareTo, StringComparison.InvariantCultureIgnoreCase);
}
public static bool InvariantEndsWith(this string compare, string compareTo)
{
return compare.EndsWith(compareTo, StringComparison.InvariantCultureIgnoreCase);
}
public static bool InvariantContains(this string compare, string compareTo)
{
return compare.IndexOf(compareTo, StringComparison.OrdinalIgnoreCase) >= 0;

View File

@@ -200,6 +200,12 @@
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
<Compile Include="Models\TemplateNode.cs" />
<Compile Include="Persistence\Querying\SqlStringExtensions.cs" />
<Compile Include="Persistence\Querying\TextColumnType.cs" />
<Compile Include="Persistence\SqlSyntax\MySqlSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlCeSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlServerSyntax.cs" />
<Compile Include="Persistence\SqlSyntax\SqlServerVersionName.cs" />
<Compile Include="PropertyEditors\PropertyCacheValue.cs" />
<Compile Include="PropertyEditors\PropertyValueCacheAttribute.cs" />
<Compile Include="PropertyEditors\PropertyValueTypeAttribute.cs" />