Compare commits

..

10 Commits

46 changed files with 2005 additions and 1530 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
@ECHO OFF
SET release=6.2.2
SET release=6.2.3
SET comment=
SET version=%release%
+1 -1
View File
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>UmbracoCms.Core</id>
<version>6.2.2</version>
<version>6.2.3</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
+1 -1
View File
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>UmbracoCms</id>
<version>6.2.2</version>
<version>6.2.3</version>
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UmbracoVersion>6.2.2</UmbracoVersion>
<UmbracoVersion>6.2.4</UmbracoVersion>
</PropertyGroup>
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
<PropertyGroup>
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("6.2.2");
private static readonly Version Version = new Version("6.2.4");
/// <summary>
/// Gets the current version of Umbraco.
+6
View File
@@ -34,6 +34,12 @@ namespace Umbraco.Core
AddInt(d.GetHashCode());
}
internal void AddString(string s)
{
if (s != null)
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
}
internal void AddCaseInsensitiveString(string s)
{
if (s != null)
@@ -99,11 +99,13 @@ namespace Umbraco.Core.Persistence.Factories
foreach (var propertyDto in dto.UmbracoPropertyDtos)
{
entity.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty
{
DataTypeControlId =
propertyDto.DataTypeControlId,
Value = propertyDto.UmbracoFile
});
{
DataTypeControlId =
propertyDto.DataTypeControlId,
Value = propertyDto.NTextValue.IsNullOrWhiteSpace()
? propertyDto.NVarcharValue
: propertyDto.NTextValue
});
}
}
+264 -187
View File
@@ -13,6 +13,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Security;
using System.Security.Permissions;
using System.Text;
@@ -1709,8 +1710,32 @@ namespace Umbraco.Core.Persistence
}
public override object ChangeType(object val) { return val; }
}
public class PocoData
{
/// <summary>
/// Container for a Memory cache object
/// </summary>
/// <remarks>
/// Better to have one memory cache instance than many so it's memory management can be handled more effectively
/// http://stackoverflow.com/questions/8463962/using-multiple-instances-of-memorycache
/// </remarks>
internal class ManagedCache
{
public ObjectCache GetCache()
{
return ObjectCache;
}
static readonly ObjectCache ObjectCache = new MemoryCache("NPoco");
}
public class PocoData
{
//USE ONLY FOR TESTING
internal static bool UseLongKeys = false;
//USE ONLY FOR TESTING - default is one hr
internal static int SlidingExpirationSeconds = 3600;
public static PocoData ForObject(object o, string primaryKeyName)
{
var t = o.GetType();
@@ -1734,7 +1759,7 @@ namespace Umbraco.Core.Persistence
#endif
return ForType(t);
}
static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
public static PocoData ForType(Type t)
{
#if !PETAPOCO_NO_DYNAMIC
@@ -1742,7 +1767,7 @@ namespace Umbraco.Core.Persistence
throw new InvalidOperationException("Can't use dynamic types with this method");
#endif
// Check cache
RWLock.EnterReadLock();
InnerLock.EnterReadLock();
PocoData pd;
try
{
@@ -1751,12 +1776,12 @@ namespace Umbraco.Core.Persistence
}
finally
{
RWLock.ExitReadLock();
InnerLock.ExitReadLock();
}
// Cache it
RWLock.EnterWriteLock();
InnerLock.EnterWriteLock();
try
{
// Check again
@@ -1770,7 +1795,7 @@ namespace Umbraco.Core.Persistence
}
finally
{
RWLock.ExitWriteLock();
InnerLock.ExitWriteLock();
}
return pd;
@@ -1851,224 +1876,237 @@ namespace Umbraco.Core.Persistence
return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
}
// Create factory function that can convert a IDataReader record into a POCO
public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r)
{
// Check cache
var key = string.Format("{0}:{1}:{2}:{3}:{4}", sql, connString, ForceDateTimesToUtc, firstColumn, countColumns);
RWLock.EnterReadLock();
try
{
// Have we already created it?
Delegate factory;
if (PocoFactories.TryGetValue(key, out factory))
return factory;
}
finally
{
RWLock.ExitReadLock();
}
// Take the writer lock
RWLock.EnterWriteLock();
//TODO: It would be nice to remove the irrelevant SQL parts - for a mapping operation anything after the SELECT clause isn't required.
// This would ensure less duplicate entries that get cached, currently both of these queries would be cached even though they are
// returning the same structured data:
// SELECT * FROM MyTable ORDER BY MyColumn
// SELECT * FROM MyTable ORDER BY MyColumn DESC
try
{
string key;
if (UseLongKeys)
{
key = string.Format("{0}:{1}:{2}:{3}:{4}", sql, connString, ForceDateTimesToUtc, firstColumn, countColumns);
}
else
{
//Create a hashed key, we don't want to store so much string data in memory
var combiner = new HashCodeCombiner();
combiner.AddCaseInsensitiveString(sql);
combiner.AddCaseInsensitiveString(connString);
combiner.AddObject(ForceDateTimesToUtc);
combiner.AddInt(firstColumn);
combiner.AddInt(countColumns);
key = combiner.GetCombinedHashCode();
}
// Check again, just in case
Delegate factory;
if (PocoFactories.TryGetValue(key, out factory))
return factory;
var objectCache = _managedCache.GetCache();
// Create the method
var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), type, new Type[] { typeof(IDataReader) }, true);
var il = m.GetILGenerator();
Func<Delegate> factory = () =>
{
// Create the method
var m = new DynamicMethod("petapoco_factory_" + objectCache.GetCount(), type, new Type[] { typeof(IDataReader) }, true);
var il = m.GetILGenerator();
#if !PETAPOCO_NO_DYNAMIC
if (type == typeof(object))
{
// var poco=new T()
il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj
if (type == typeof(object))
{
// var poco=new T()
il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj
MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");
MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
var srcType = r.GetFieldType(i);
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
var srcType = r.GetFieldType(i);
il.Emit(OpCodes.Dup); // obj, obj
il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname
il.Emit(OpCodes.Dup); // obj, obj
il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname
// Get the converter
Func<object, object> converter = null;
if (Database.Mapper != null)
converter = Database.Mapper.GetFromDbConverter(null, srcType);
if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
// Get the converter
Func<object, object> converter = null;
if (Database.Mapper != null)
converter = Database.Mapper.GetFromDbConverter(null, srcType);
if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Setup stack for call to converter
AddConverterToStack(il, converter);
// r[i]
il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr
il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value
// r[i]
il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr
il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value
// Convert DBNull to null
il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value
il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null)
var lblNotNull = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value
il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
if (converter != null)
il.Emit(OpCodes.Pop); // obj, obj, fieldname,
il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
if (converter != null)
{
var lblReady = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblReady);
il.MarkLabel(lblNotNull);
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblReady);
}
else
{
il.MarkLabel(lblNotNull);
}
// Convert DBNull to null
il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value
il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null)
var lblNotNull = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value
il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
if (converter != null)
il.Emit(OpCodes.Pop); // obj, obj, fieldname,
il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
if (converter != null)
{
var lblReady = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblReady);
il.MarkLabel(lblNotNull);
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblReady);
}
else
{
il.MarkLabel(lblNotNull);
}
il.Emit(OpCodes.Callvirt, fnAdd);
}
}
else
il.Emit(OpCodes.Callvirt, fnAdd);
}
}
else
#endif
if (type.IsValueType || type == typeof(string) || type == typeof(byte[]))
{
// Do we need to install a converter?
var srcType = r.GetFieldType(0);
var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type);
if (type.IsValueType || type == typeof(string) || type == typeof(byte[]))
{
// Do we need to install a converter?
var srcType = r.GetFieldType(0);
var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type);
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool
var lblCont = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblCont);
il.Emit(OpCodes.Ldnull); // null
var lblFin = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblFin);
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool
var lblCont = il.DefineLabel();
il.Emit(OpCodes.Brfalse_S, lblCont);
il.Emit(OpCodes.Ldnull); // null
var lblFin = il.DefineLabel();
il.Emit(OpCodes.Br_S, lblFin);
il.MarkLabel(lblCont);
il.MarkLabel(lblCont);
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Setup stack for call to converter
AddConverterToStack(il, converter);
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnGetValue); // value
il.Emit(OpCodes.Ldarg_0); // rdr
il.Emit(OpCodes.Ldc_I4_0); // rdr,0
il.Emit(OpCodes.Callvirt, fnGetValue); // value
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
il.MarkLabel(lblFin);
il.Emit(OpCodes.Unbox_Any, type); // value converted
}
else
{
// var poco=new T()
il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
il.MarkLabel(lblFin);
il.Emit(OpCodes.Unbox_Any, type); // value converted
}
else
{
// var poco=new T()
il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
// Get the PocoColumn for this db column, ignore if not known
PocoColumn pc;
if (!Columns.TryGetValue(r.GetName(i), out pc))
continue;
// Enumerate all fields generating a set assignment for the column
for (int i = firstColumn; i < firstColumn + countColumns; i++)
{
// Get the PocoColumn for this db column, ignore if not known
PocoColumn pc;
if (!Columns.TryGetValue(r.GetName(i), out pc))
continue;
// Get the source type for this column
var srcType = r.GetFieldType(i);
var dstType = pc.PropertyInfo.PropertyType;
// Get the source type for this column
var srcType = r.GetFieldType(i);
var dstType = pc.PropertyInfo.PropertyType;
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // poco,rdr
il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
var lblNext = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, lblNext); // poco
// "if (!rdr.IsDBNull(i))"
il.Emit(OpCodes.Ldarg_0); // poco,rdr
il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
var lblNext = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, lblNext); // poco
il.Emit(OpCodes.Dup); // poco,poco
il.Emit(OpCodes.Dup); // poco,poco
// Do we need to install a converter?
var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType);
// Do we need to install a converter?
var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType);
// Fast
bool Handled = false;
if (converter == null)
{
var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
if (valuegetter != null
&& valuegetter.ReturnType == srcType
&& (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
{
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, valuegetter); // *,value
// Fast
bool Handled = false;
if (converter == null)
{
var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
if (valuegetter != null
&& valuegetter.ReturnType == srcType
&& (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
{
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, valuegetter); // *,value
// Convert to Nullable
if (Nullable.GetUnderlyingType(dstType) != null)
{
il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
}
// Convert to Nullable
if (Nullable.GetUnderlyingType(dstType) != null)
{
il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
}
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
Handled = true;
}
}
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
Handled = true;
}
}
// Not so fast
if (!Handled)
{
// Setup stack for call to converter
AddConverterToStack(il, converter);
// Not so fast
if (!Handled)
{
// Setup stack for call to converter
AddConverterToStack(il, converter);
// "value = rdr.GetValue(i)"
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // *,value
// "value = rdr.GetValue(i)"
il.Emit(OpCodes.Ldarg_0); // *,rdr
il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
il.Emit(OpCodes.Callvirt, fnGetValue); // *,value
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Call the converter
if (converter != null)
il.Emit(OpCodes.Callvirt, fnInvoke);
// Assign it
il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
}
// Assign it
il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
}
il.MarkLabel(lblNext);
}
il.MarkLabel(lblNext);
}
var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
if (fnOnLoaded != null)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Callvirt, fnOnLoaded);
}
}
var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
if (fnOnLoaded != null)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Callvirt, fnOnLoaded);
}
}
il.Emit(OpCodes.Ret);
il.Emit(OpCodes.Ret);
// return it
var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
return del;
};
//lazy usage of AddOrGetExisting ref: http://stackoverflow.com/questions/10559279/how-to-deal-with-costly-building-operations-using-memorycache/15894928#15894928
var newValue = new Lazy<Delegate>(factory);
// the line belows returns existing item or adds the new value if it doesn't exist
var value = (Lazy<Delegate>)objectCache.AddOrGetExisting(key, newValue, new CacheItemPolicy
{
//sliding expiration of 1 hr, if the same key isn't used in this
// timeframe it will be removed from the cache
SlidingExpiration = new TimeSpan(0, 0, SlidingExpirationSeconds)
});
return (value ?? newValue).Value; // Lazy<T> handles the locking itself
// Cache it, return it
var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
PocoFactories.Add(key, del);
return del;
}
finally
{
RWLock.ExitWriteLock();
}
}
private static void AddConverterToStack(ILGenerator il, Func<object, object> converter)
@@ -2144,7 +2182,7 @@ namespace Umbraco.Core.Persistence
return default(T);
}
ManagedCache _managedCache = new ManagedCache();
static Dictionary<Type, PocoData> m_PocoDatas = new Dictionary<Type, PocoData>();
static List<Func<object, object>> m_Converters = new List<Func<object, object>>();
static MethodInfo fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
@@ -2156,7 +2194,46 @@ namespace Umbraco.Core.Persistence
public string[] QueryColumns { get; private set; }
public TableInfo TableInfo { get; private set; }
public Dictionary<string, PocoColumn> Columns { get; private set; }
Dictionary<string, Delegate> PocoFactories = new Dictionary<string, Delegate>();
static System.Threading.ReaderWriterLockSlim InnerLock = new System.Threading.ReaderWriterLockSlim();
/// <summary>
/// Returns a report of the current cache being utilized by PetaPoco
/// </summary>
/// <returns></returns>
public static string PrintDebugCacheReport(out double totalBytes, out IEnumerable<string> allKeys)
{
var managedCache = new ManagedCache();
var sb = new StringBuilder();
sb.AppendLine("m_PocoDatas:");
foreach (var pocoData in m_PocoDatas)
{
sb.AppendFormat("\t{0}\n", pocoData.Key);
sb.AppendFormat("\t\tTable:{0} - Col count:{1}\n", pocoData.Value.TableInfo.TableName, pocoData.Value.QueryColumns.Length);
}
var cache = managedCache.GetCache();
allKeys = cache.Select(x => x.Key).ToArray();
sb.AppendFormat("\tTotal Poco data count:{0}\n", allKeys.Count());
var keys = string.Join("", cache.Select(x => x.Key));
//Bytes in .Net are stored as utf-16 = unicode little endian
totalBytes = Encoding.Unicode.GetByteCount(keys);
sb.AppendFormat("\tTotal byte for keys:{0}\n", totalBytes);
sb.AppendLine("\tAll Poco cache items:");
foreach (var item in cache)
{
sb.AppendFormat("\t\t Key -> {0}\n", item.Key);
sb.AppendFormat("\t\t Value -> {0}\n", item.Value);
}
sb.AppendLine("-------------------END REPORT------------------------");
return sb.ToString();
}
}
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence
var expresionist = new PocoToSqlExpressionHelper<T>();
string whereExpression = expresionist.Visit(predicate);
return sql.Where(whereExpression);
return sql.Where(whereExpression, expresionist.GetSqlParameters());
}
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember)
@@ -1,28 +1,562 @@
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<T> : BaseExpressionHelper
{
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)
{
var m = lambda.Body as MemberExpression;
if (m.Expression != null)
{
//This deals with members that are boolean (i.e. x => IsTrashed )
string r = VisitMemberAccess(m);
SqlParameters.Add(true);
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);
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 + " " + operand + " " + right;
}
}
protected virtual List<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
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<Func<object>>(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<Object> 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:
var o = Visit(u.Operand);
//use a Not equal operator instead of <> since we don't know that <> works in all sql servers
switch (u.Operand.NodeType)
{
case ExpressionType.MemberAccess:
//In this case it wil be a false property , i.e. x => !Trashed
SqlParameters.Add(true);
return string.Format("NOT ({0} = @0)", o);
default:
//In this case it could be anything else, such as: x => !x.Path.StartsWith("-20")
return string.Format("NOT ({0})", o);
}
default:
return Visit(u.Operand);
}
}
protected virtual string VisitNewArray(NewArrayExpression na)
{
List<Object> 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<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> 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)
{
//Here's what happens with a MethodCallExpression:
// If a method is called that contains a single argument,
// then m.Object is the object on the left hand side of the method call, example:
// x.Path.StartsWith(content.Path)
// m.Object = x.Path
// and m.Arguments.Length == 1, therefor m.Arguments[0] == content.Path
// If a method is called that contains multiple arguments, then m.Object == null and the
// m.Arguments collection contains the left hand side of the method call, example:
// x.Path.SqlStartsWith(content.Path, TextColumnType.NVarchar)
// m.Object == null
// m.Arguments.Length == 3, therefor, m.Arguments[0] == x.Path, m.Arguments[1] == content.Path, m.Arguments[2] == TextColumnType.NVarchar
// So, we need to cater for these scenarios.
var objectForMethod = m.Object ?? m.Arguments[0];
var visitedObjectForMethod = Visit(objectForMethod);
var methodArgs = m.Object == null
? m.Arguments.Skip(1).ToArray()
: m.Arguments.ToArray();
switch (m.Method.Name)
{
case "ToString":
SqlParameters.Add(objectForMethod.ToString());
return string.Format("@{0}", SqlParameters.Count - 1);
case "ToUpper":
return string.Format("upper({0})", visitedObjectForMethod);
case "ToLower":
return string.Format("lower({0})", visitedObjectForMethod);
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":
string compareValue;
if (methodArgs[0].NodeType != ExpressionType.Constant)
{
//This occurs when we are getting a value from a non constant such as: x => x.Path.StartsWith(content.Path)
// So we'll go get the value:
var member = Expression.Convert(methodArgs[0], typeof(object));
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
compareValue = getter().ToString();
}
else
{
compareValue = methodArgs[0].ToString();
}
//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
if (methodArgs[0].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(methodArgs[0].Type))
{
throw new NotSupportedException("An array Contains method is not supported");
}
//default column type
var colType = TextColumnType.NVarchar;
//then check if the col type argument has been passed to the current method (this will be the case for methods like
// SqlContains and other Sql methods)
if (methodArgs.Length > 1)
{
var colTypeArg = methodArgs.FirstOrDefault(x => x is ConstantExpression && x.Type == typeof(TextColumnType));
if (colTypeArg != null)
{
colType = (TextColumnType)((ConstantExpression)colTypeArg).Value;
}
}
return HandleStringComparison(visitedObjectForMethod, compareValue, 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<Func<object>>(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()))));
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());
//}
}
/// <summary>
/// Logic that is shared with the expression helpers
/// </summary>
internal class BaseExpressionHelper
internal class BaseExpressionHelper
{
protected List<object> SqlParameters = new List<object>();
public object[] GetSqlParameters()
{
return SqlParameters.ToArray();
}
protected string HandleStringComparison(string col, string val, string verb, TextColumnType columnType)
{
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
@@ -44,61 +578,54 @@ namespace Umbraco.Core.Persistence.Querying
}
}
public virtual string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
{
if (value == null) return "NULL";
//public virtual string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
//{
// if (value == null) return "NULL";
if (escapeCallback == null)
{
escapeCallback = EscapeParam;
}
if (shouldQuoteCallback == null)
{
shouldQuoteCallback = ShouldQuoteValue;
}
// if (escapeCallback == null)
// {
// escapeCallback = EscapeParam;
// }
// if (shouldQuoteCallback == null)
// {
// shouldQuoteCallback = ShouldQuoteValue;
// }
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
{
//if (TypeSerializer.CanCreateFromString(fieldType))
//{
// return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
//}
// if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
// {
// //if (TypeSerializer.CanCreateFromString(fieldType))
// //{
// // return "'" + escapeCallback(TypeSerializer.SerializeToString(value)) + "'";
// //}
throw new NotSupportedException(
string.Format("Property of type: {0} is not supported", fieldType.FullName));
}
// throw new NotSupportedException(
// string.Format("Property of type: {0} is not supported", fieldType.FullName));
// }
if (fieldType == typeof(int))
return ((int)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(int))
// return ((int)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(float))
return ((float)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(float))
// return ((float)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(double))
return ((double)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(double))
// return ((double)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(decimal))
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
// if (fieldType == typeof(decimal))
// return ((decimal)value).ToString(CultureInfo.InvariantCulture);
if (fieldType == typeof(DateTime))
{
return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'";
}
// if (fieldType == typeof(DateTime))
// {
// return "'" + escapeCallback(((DateTime)value).ToIsoString()) + "'";
// }
if (fieldType == typeof(bool))
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
// if (fieldType == typeof(bool))
// return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
return shouldQuoteCallback(fieldType)
? "'" + escapeCallback(value) + "'"
: value.ToString();
}
public virtual string EscapeParam(object paramValue)
{
return paramValue == null
? string.Empty
: SqlSyntaxContext.SqlSyntaxProvider.EscapeString(paramValue.ToString());
}
// return shouldQuoteCallback(fieldType)
// ? "'" + escapeCallback(value) + "'"
// : value.ToString();
//}
public virtual bool ShouldQuoteValue(Type fieldType)
{
@@ -107,16 +634,12 @@ namespace Umbraco.Core.Persistence.Querying
protected virtual string RemoveQuote(string exp)
{
if (exp.StartsWith("'") && exp.EndsWith("'"))
{
exp = exp.Remove(0, 1);
exp = exp.Remove(exp.Length - 1, 1);
}
return exp;
}
protected virtual string RemoveQuoteFromAlias(string exp)
{
//if (exp.StartsWith("'") && exp.EndsWith("'"))
//{
// exp = exp.Remove(0, 1);
// exp = exp.Remove(exp.Length - 1, 1);
//}
//return exp;
if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
&&
@@ -127,5 +650,18 @@ namespace Umbraco.Core.Persistence.Querying
}
return exp;
}
//protected virtual string RemoveQuoteFromAlias(string exp)
//{
// if ((exp.StartsWith("\"") || exp.StartsWith("`") || exp.StartsWith("'"))
// &&
// (exp.EndsWith("\"") || exp.EndsWith("`") || exp.EndsWith("'")))
// {
// exp = exp.Remove(0, 1);
// exp = exp.Remove(exp.Length - 1, 1);
// }
// return exp;
//}
}
}
@@ -1,10 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Umbraco.Core.Persistence.Querying
{
/// <summary>
/// SD: This is a horrible hack but unless we break compatibility with anyone who's actually implemented IQuery{T} there's not much we can do.
/// The IQuery{T} interface is useless without having a GetWhereClauses method and cannot be used for tests.
/// We have to wait till v8 to make this change I suppose.
/// </summary>
internal static class QueryExtensions
{
/// <summary>
/// Returns all translated where clauses and their sql parameters
/// </summary>
/// <returns></returns>
public static IEnumerable<Tuple<string, object[]>> GetWhereClauses<T>(this IQuery<T> query)
{
var q = query as Query<T>;
if (q == null)
{
throw new NotSupportedException(typeof(IQuery<T>) + " cannot be cast to " + typeof(Query<T>));
}
return q.GetWhereClauses();
}
}
/// <summary>
/// Represents a query for building Linq translatable SQL queries
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IQuery<T>
{
/// <summary>
/// Adds a where clause to the query
/// </summary>
/// <param name="predicate"></param>
/// <returns>This instance so calls to this method are chainable</returns>
IQuery<T> Where(Expression<Func<T, bool>> predicate);
}
}
@@ -10,149 +10,21 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper
internal class ModelToSqlExpressionHelper<T> : BaseExpressionHelper<T>
{
private string sep = " ";
private BaseMapper _mapper;
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);
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);
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);
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;
@@ -168,324 +40,18 @@ namespace Umbraco.Core.Persistence.Querying
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o != null ? o.GetType() : null);
SqlParameters.Add(o);
return string.Format("@{0}", SqlParameters.Count - 1);
//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<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o.GetType());
}
catch (System.InvalidOperationException)
{ // FieldName ?
List<Object> 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";
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);
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> 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<Func<object>>(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]")
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(e, e.GetType()));
}
else
{
var listArgs = e as IList<Object>;
foreach (Object el in listArgs)
{
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:
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<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
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<Object> 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<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> 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
return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length;
}
//protected bool IsFieldName(string quotedExp)
//{
// //Not entirely sure this is reliable, but its better then simply returning true
// return quotedExp.LastIndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1 != quotedExp.Length;
//}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@@ -8,159 +9,28 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Querying
{
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper
internal class PocoToSqlExpressionHelper<T> : BaseExpressionHelper<T>
{
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);
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);
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);
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;
}
@@ -168,303 +38,14 @@ namespace Umbraco.Core.Persistence.Querying
var lambda = Expression.Lambda<Func<object>>(member);
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o != null ? o.GetType() : null);
SqlParameters.Add(o);
return string.Format("@{0}", SqlParameters.Count - 1);
//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<Func<object>>(member);
try
{
var getter = lambda.Compile();
object o = getter();
return GetQuotedValue(o, o.GetType());
}
catch (System.InvalidOperationException)
{ // FieldName ?
List<Object> 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";
else if (c.Value.GetType() == typeof(bool))
{
object o = GetQuotedValue(c.Value, c.Value.GetType());
return string.Format("({0}={1})", GetQuotedTrueValue(), o);
}
else
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);
}
}
protected virtual string VisitMethodCall(MethodCallExpression m)
{
List<Object> 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<Func<object>>(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]")
{
sIn.AppendFormat("{0}{1}",
sIn.Length > 0 ? "," : "",
GetQuotedValue(e, e.GetType()));
}
else
{
var listArgs = e as IList<Object>;
foreach (Object el in listArgs)
{
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:
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<Object> VisitExpressionList(ReadOnlyCollection<Expression> original)
{
var list = new List<Object>();
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<Object> 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<Object> VisitNewArrayFromExpressionList(NewArrayExpression na)
{
List<Object> 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);
@@ -472,34 +53,10 @@ namespace Umbraco.Core.Persistence.Querying
SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(pocoData.TableInfo.TableName),
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)
{
return true;
}
//protected bool IsFieldName(string quotedExp)
//{
// return true;
//}
}
}
+27 -17
View File
@@ -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
/// <typeparam name="T"></typeparam>
public class Query<T> : IQuery<T>
{
//private readonly ExpressionHelper<T> _expresionist = new ExpressionHelper<T>();
private readonly ModelToSqlExpressionHelper<T> _expresionist = new ModelToSqlExpressionHelper<T>();
private readonly List<string> _wheres = new List<string>();
public Query()
: base()
{
}
private readonly List<Tuple<string, object[]>> _wheres = new List<Tuple<string, object[]>>();
/// <summary>
/// Helper method to be used instead of manually creating an instance
/// </summary>
public static IQuery<T> Builder
{
get
{
return new Query<T>();
}
get { return new Query<T>(); }
}
/// <summary>
/// Adds a where clause to the query
/// </summary>
/// <param name="predicate"></param>
/// <returns>This instance so calls to this method are chainable</returns>
public virtual IQuery<T> Where(Expression<Func<T, bool>> predicate)
{
if (predicate != null)
{
string whereExpression = _expresionist.Visit(predicate);
_wheres.Add(whereExpression);
var expressionHelper = new ModelToSqlExpressionHelper<T>();
string whereExpression = expressionHelper.Visit(predicate);
_wheres.Add(new Tuple<string, object[]>(whereExpression, expressionHelper.GetSqlParameters()));
}
return this;
}
public List<string> WhereClauses()
/// <summary>
/// Returns all translated where clauses and their sql parameters
/// </summary>
/// <returns></returns>
public IEnumerable<Tuple<string, object[]>> GetWhereClauses()
{
return _wheres;
}
[Obsolete("This is no longer used, use the GetWhereClauses method which includes the SQL parameters")]
public List<string> WhereClauses()
{
return _wheres.Select(x => x.Item1).ToList();
}
}
}
@@ -15,14 +15,10 @@ namespace Umbraco.Core.Persistence.Querying
if (sql == null)
throw new Exception("Sql cannot be null");
var query1 = query as Query<T>;
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);
}
}
@@ -66,15 +66,31 @@ namespace Umbraco.Core.Persistence.Repositories
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key).Append(GetGroupBy(isContent, isMedia));
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
var sql = GetFullSqlForEntityType(key, isContent, isMedia, objectTypeId);
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
return entity;
return entities.FirstOrDefault();
}
else
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
}
public virtual IUmbracoEntity Get(int id)
@@ -94,61 +110,90 @@ namespace Umbraco.Core.Persistence.Repositories
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id).Append(GetGroupBy(isContent, isMedia));
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var sql = GetFullSqlForEntityType(id, isContent, isMedia, objectTypeId);
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entities.FirstOrDefault();
}
else
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
return entity;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
}
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId, params int[] ids)
{
if (ids.Any())
{
foreach (var id in ids)
return PerformGetAll(objectTypeId, sql1 => sql1.Where(" umbracoNode.id in (@ids)", new {ids = ids}));
}
else
{
return PerformGetAll(objectTypeId);
}
}
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId, params Guid[] keys)
{
if (keys.Any())
{
return PerformGetAll(objectTypeId, sql1 => sql1.Where(" umbracoNode.uniqueID in (@keys)", new { keys = keys }));
}
else
{
return PerformGetAll(objectTypeId);
}
}
private IEnumerable<IUmbracoEntity> PerformGetAll(Guid objectTypeId, Action<Sql> filter = null)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, filter);
var factory = new UmbracoEntityFactory();
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
foreach (var entity in entities)
{
yield return Get(id, objectTypeId);
yield return entity;
}
}
else
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia));
var factory = new UmbracoEntityFactory();
if (isMedia)
var dtos = _work.Database.Fetch<dynamic>(sql);
foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto)))
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
foreach (var entity in entities)
{
yield return entity;
}
}
else
{
var dtos = _work.Database.Fetch<dynamic>(sql);
foreach (var entity in dtos.Select(dto => factory.BuildEntityFromDynamic(dto)))
{
yield return entity;
}
yield return entity;
}
}
}
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query)
{
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>) query).WhereClauses()));
var sqlClause = GetBase(false, false, wheres);
var sqlClause = GetBase(false, false, null);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy(false, false));
@@ -162,28 +207,41 @@ namespace Umbraco.Core.Persistence.Repositories
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>)query).WhereClauses()));
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, wheres, objectTypeId);
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, null, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia));
var entitySql = translator.Translate();
var factory = new UmbracoEntityFactory();
if (isMedia)
{
var wheres = query.GetWhereClauses().ToArray();
var mediaSql = GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), sql =>
{
//adds the additional filters
foreach (var whereClause in wheres)
{
sql.Where(whereClause.Item1, whereClause.Item2);
}
});
//treat media differently for now
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
new UmbracoEntityRelator().Map, mediaSql);
return entities;
}
else
{
//use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData
var dtos = _work.Database.Fetch<dynamic>(sql);
var finalSql = entitySql.Append(GetGroupBy(isContent, false));
var dtos = _work.Database.Fetch<dynamic>(finalSql);
return dtos.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
}
}
@@ -193,7 +251,68 @@ namespace Umbraco.Core.Persistence.Repositories
#region Sql Statements
protected virtual Sql GetBase(bool isContent, bool isMedia, string additionWhereStatement = "")
protected Sql GetFullSqlForEntityType(Guid key, bool isContent, bool isMedia, Guid objectTypeId)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
protected Sql GetFullSqlForEntityType(int id, bool isContent, bool isMedia, Guid objectTypeId)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
protected Sql GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectTypeId, Action<Sql> filter)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, filter, objectTypeId);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), filter);
}
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
{
//this will add any dataNvarchar property to the output which can be added to the additional properties
var joinSql = new Sql()
.Select("contentNodeId, versionId, dataNvarchar, dataNtext, controlId, alias as propertyTypeAlias")
.From<PropertyDataDto>()
.InnerJoin<NodeDto>()
.On<PropertyDataDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new {nodeObjectType = Constants.ObjectTypes.Media});
if (filter != null)
{
filter(joinSql);
}
//We're going to create a query to query against the entity SQL
// because we cannot group by nText columns and we have a COUNT in the entitySql we cannot simply left join
// the entitySql query, we have to join the wrapped query to get the ntext in the result
var wrappedSql = new Sql("SELECT * FROM (")
.Append(entitySql)
.Append(new Sql(") tmpTbl LEFT JOIN ("))
.Append(joinSql)
.Append(new Sql(") as property ON id = property.contentNodeId"))
.OrderBy("sortOrder");
return wrappedSql;
}
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter)
{
var columns = new List<object>
{
@@ -221,21 +340,15 @@ namespace Umbraco.Core.Persistence.Repositories
columns.Add("contenttype.isContainer");
}
if (isMedia)
{
columns.Add("property.dataNvarchar as umbracoFile");
columns.Add("property.controlId");
}
//Creates an SQL query to return a single row for the entity
var sql = new Sql()
var entitySql = new Sql()
.Select(columns.ToArray())
.From("umbracoNode umbracoNode")
.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
.From("umbracoNode umbracoNode");
if (isContent || isMedia)
{
sql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published")
@@ -245,60 +358,56 @@ namespace Umbraco.Core.Persistence.Repositories
.On("umbracoNode.id = latest.nodeId");
}
if (isMedia)
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
if (customFilter != null)
{
sql.LeftJoin(
"(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData " +
"INNER JOIN umbracoNode ON cmsPropertyData.contentNodeId = umbracoNode.id " +
"INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid " +
"INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId "+
"WHERE umbracoNode.nodeObjectType = '" + Constants.ObjectTypes.Media + "'" + additionWhereStatement + ") as property")
.On("umbracoNode.id = property.contentNodeId");
customFilter(entitySql);
}
return sql;
return entitySql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid nodeObjectType)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Action<Sql> filter, Guid nodeObjectType)
{
var sql = baseQuery(isContent, isMedia, additionWhereStatement)
var sql = baseQuery(isContent, isMedia, filter)
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType });
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, int id)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, int id)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '" + id + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id", new { Id = id })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key })
.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, int id)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '" + id + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
new { Id = id, NodeObjectType = nodeObjectType });
new {Id = id, NodeObjectType = nodeObjectType});
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key)
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid nodeObjectType, Guid key)
{
var sql = baseQuery(isContent, isMedia, " AND umbracoNode.uniqueID = '" + key + "'")
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType",
new { UniqueID = key, NodeObjectType = nodeObjectType });
return sql;
}
protected virtual Sql GetGroupBy(bool isContent, bool isMedia)
protected virtual Sql GetGroupBy(bool isContent, bool isMedia, bool includeSort = true)
{
var columns = new List<object>
{
@@ -324,19 +433,18 @@ namespace Umbraco.Core.Persistence.Repositories
columns.Add("contenttype.thumbnail");
columns.Add("contenttype.isContainer");
}
var sql = new Sql()
.GroupBy(columns.ToArray());
if (isMedia)
if (includeSort)
{
columns.Add("property.dataNvarchar");
columns.Add("property.controlId");
sql = sql.OrderBy("umbracoNode.sortOrder");
}
var sql = new Sql()
.GroupBy(columns.ToArray())
.OrderBy("umbracoNode.sortOrder");
return sql;
}
#endregion
/// <summary>
@@ -384,10 +492,16 @@ namespace Umbraco.Core.Persistence.Repositories
[Column("controlId")]
public Guid DataTypeControlId { get; set; }
[Column("umbracoFile")]
public string UmbracoFile { get; set; }
}
[Column("propertyTypeAlias")]
public string PropertyAlias { get; set; }
[Column("dataNvarchar")]
public string NVarcharValue { get; set; }
[Column("dataNtext")]
public string NTextValue { get; set; }
}
/// <summary>
/// This is a special relator in that it is not returning a DTO but a real resolved entity and that it accepts
/// a dynamic instance.
@@ -418,10 +532,12 @@ namespace Umbraco.Core.Persistence.Repositories
Current.UmbracoProperties = new List<UmbracoEntity.UmbracoProperty>();
}
Current.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty
{
DataTypeControlId = p.DataTypeControlId,
Value = p.UmbracoFile
});
{
DataTypeControlId = p.DataTypeControlId,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue
});
// Return null to indicate we're not done with this UmbracoEntity yet
return null;
}
@@ -433,7 +549,7 @@ namespace Umbraco.Core.Persistence.Repositories
var prev = Current;
// Setup the new current UmbracoEntity
Current = _factory.BuildEntityFromDynamic(a);
//add the property/create the prop list if null
@@ -442,7 +558,9 @@ namespace Umbraco.Core.Persistence.Repositories
new UmbracoEntity.UmbracoProperty
{
DataTypeControlId = p.DataTypeControlId,
Value = p.UmbracoFile
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue
}
};
@@ -202,13 +202,13 @@ namespace Umbraco.Core.Persistence.Repositories
//find the member by username
var memberSql = new Sql();
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
var escapedUsername = PetaPocoExtensions.EscapeAtSymbols(username);
memberSql.Select("umbracoNode.id")
.From<NodeDto>()
.InnerJoin<MemberDto>()
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
.Where<MemberDto>(x => x.LoginName == escapedUsername);
.Where<MemberDto>(x => x.LoginName == username);
var memberIdUsername = Database.Fetch<int?>(memberSql).FirstOrDefault();
if (memberIdUsername.HasValue == false)
{
@@ -279,6 +279,9 @@ namespace Umbraco.Core.Persistence.Repositories
public void AssignRolesInternal(int[] memberIds, string[] roleNames)
{
//ensure they're unique
memberIds = memberIds.Distinct().ToArray();
//create the missing roles first
var existingSql = new Sql()
@@ -520,10 +520,10 @@ namespace Umbraco.Core.Persistence.Repositories
public bool Exists(string username)
{
var sql = new Sql();
var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username);
sql.Select("COUNT(*)")
.From<MemberDto>()
.Where<MemberDto>(x => x.LoginName == escapedUserName);
.Where<MemberDto>(x => x.LoginName == username);
return Database.ExecuteScalar<int>(sql) > 0;
}
@@ -262,10 +262,10 @@ namespace Umbraco.Core.Persistence.Repositories
public bool Exists(string username)
{
var sql = new Sql();
var escapedUserName = PetaPocoExtensions.EscapeAtSymbols(username);
sql.Select("COUNT(*)")
.From<UserDto>()
.Where<UserDto>(x => x.UserName == escapedUserName);
.Where<UserDto>(x => x.UserName == username);
return Database.ExecuteScalar<int>(sql) > 0;
}
@@ -188,6 +188,8 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="updateDate"></param>
/// <returns></returns>
protected PropertyCollection GetPropertyCollection(int id, Guid versionId, IContentTypeComposition contentType, DateTime createDate, DateTime updateDate)
{
var sql = new Sql();
sql.Select("*")
@@ -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);
@@ -0,0 +1,120 @@
using System;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.SqlSyntax
{
/// <summary>
/// Abstract class for defining MS sql implementations
/// </summary>
/// <typeparam name="TSyntax"></typeparam>
public abstract class MicrosoftSqlSyntaxProviderBase<TSyntax> : SqlSyntaxProviderBase<TSyntax>
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");
}
}
}
}
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlServerCe.4.0")]
public class SqlCeSyntaxProvider : SqlSyntaxProviderBase<SqlCeSyntaxProvider>
public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlCeSyntaxProvider>
{
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}'"; } }
}
}
@@ -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
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlClient")]
public class SqlServerSyntaxProvider : SqlSyntaxProviderBase<SqlServerSyntaxProvider>
public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlServerSyntaxProvider>
{
public SqlServerSyntaxProvider()
{
@@ -33,91 +32,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// Gets/sets the version of the current SQL server instance
/// </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 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<string> 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}'"; } }
}
}
@@ -33,6 +33,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
};
}
public string GetWildcardPlaceholder()
{
return "%";
}
public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})";
@@ -108,34 +113,51 @@ 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());
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());
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());
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.
return string.Format("upper({0}) like '{1}'", column, value.ToUpper());
return string.Format("upper({0}) LIKE '{1}'", column, value.ToUpper());
}
public virtual string GetQuotedTableName(string tableName)
@@ -10,12 +10,12 @@
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-3876
/// </remarks>
public static string GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery)
public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery)
{
return string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)",
sqlProvider.GetQuotedTableName(tableName),
sqlProvider.GetQuotedColumnName(columnName),
subQuery.SQL);
return new Sql(string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)",
sqlProvider.GetQuotedTableName(tableName),
sqlProvider.GetQuotedColumnName(columnName),
subQuery.SQL), subQuery.Arguments);
}
}
+6 -1
View File
@@ -484,6 +484,10 @@ namespace Umbraco.Core.Services
public IEnumerable<IContent> GetDescendants(int id)
{
var content = GetById(id);
if (content == null)
{
return Enumerable.Empty<IContent>();
}
return GetDescendants(content);
}
@@ -496,7 +500,8 @@ namespace Umbraco.Core.Services
{
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(content.Path) && x.Id != content.Id);
var pathMatch = content.Path + ",";
var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != content.Id);
var contents = repository.GetByQuery(query);
return contents;
+2 -1
View File
@@ -252,7 +252,8 @@ namespace Umbraco.Core.Services
using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork()))
{
var entity = repository.Get(id);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id);
var pathMatch = entity.Path + ",";
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id);
var entities = repository.GetByQuery(query);
return entities;
+7 -2
View File
@@ -402,6 +402,10 @@ namespace Umbraco.Core.Services
public IEnumerable<IMedia> GetDescendants(int id)
{
var media = GetById(id);
if (media == null)
{
return Enumerable.Empty<IMedia>();
}
return GetDescendants(media);
}
@@ -415,7 +419,8 @@ namespace Umbraco.Core.Services
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
var query = Query<IMedia>.Builder.Where(x => x.Path.StartsWith(media.Path) && x.Id != media.Id);
var pathMatch = media.Path + ",";
var query = Query<IMedia>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != media.Id);
var medias = repository.GetByQuery(query);
return medias;
@@ -1198,4 +1203,4 @@ namespace Umbraco.Core.Services
public static event TypedEventHandler<IMediaService, RecycleBinEventArgs> EmptiedRecycleBin;
#endregion
}
}
}
+1
View File
@@ -233,6 +233,7 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\AssignMissingPrimaryForMySqlKeys2.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\ChangePasswordColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\UpdateToNewMemberPropertyAliases.cs" />
<Compile Include="Persistence\SqlSyntax\MicrosoftSqlSyntaxProviderBase.cs" />
<Compile Include="Persistence\Querying\SqlStringExtensions.cs" />
<Compile Include="Persistence\Querying\StringPropertyMatchType.cs" />
<Compile Include="Persistence\Querying\TextColumnType.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<dynamic>(
// "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<dynamic>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES");
// // r1 = i1.Select(x => x.TABLE_NAME).Cast<string>().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<dynamic>("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<int>(() => contentType2.Id), 0, contentType2.Alias),
// new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
// };
// contentType2.AllowedContentTypes = new[]
// {
// new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
// new ContentTypeSort(new Lazy<int>(() => contentType3.Id), 1, contentType3.Alias)
// };
// contentType3.AllowedContentTypes = new[]
// {
// new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias),
// new ContentTypeSort(new Lazy<int>(() => 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));
// }
// }
// }
//}
@@ -1,14 +1,195 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Tests.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence
{
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture, NUnit.Framework.Ignore]
public class PetaPocoCachesTest : BaseServiceTest
{
/// <summary>
/// This tests the peta poco caches
/// </summary>
/// <remarks>
/// This test WILL fail. This is because we cannot stop PetaPoco from creating more cached items for queries such as
/// ContentTypeRepository.GetAll(1,2,3,4);
/// when combined with other GetAll queries that pass in an array of Ids, each query generated for different length
/// arrays will produce a unique query which then gets added to the cache.
///
/// This test confirms this, if you analyze the DIFFERENCE output below you can see why the cached queries grow.
/// </remarks>
[Test]
public void Check_Peta_Poco_Caches()
{
var result = new List<Tuple<double, int, IEnumerable<string>>>();
Database.PocoData.UseLongKeys = true;
for (int i = 0; i < 2; i++)
{
int id1, id2, id3;
string alias;
CreateStuff(out id1, out id2, out id3, out alias);
QueryStuff(id1, id2, id3, alias);
double totalBytes1;
IEnumerable<string> keys;
Console.Write(Database.PocoData.PrintDebugCacheReport(out totalBytes1, out keys));
result.Add(new Tuple<double, int, IEnumerable<string>>(totalBytes1, keys.Count(), keys));
}
for (int index = 0; index < result.Count; index++)
{
var tuple = result[index];
Console.WriteLine("Bytes: {0}, Delegates: {1}", tuple.Item1, tuple.Item2);
if (index != 0)
{
Console.WriteLine("----------------DIFFERENCE---------------------");
var diff = tuple.Item3.Except(result[index - 1].Item3);
foreach (var d in diff)
{
Console.WriteLine(d);
}
}
}
var allByteResults = result.Select(x => x.Item1).Distinct();
var totalKeys = result.Select(x => x.Item2).Distinct();
Assert.AreEqual(1, allByteResults.Count());
Assert.AreEqual(1, totalKeys.Count());
}
[Test]
public void Verify_Memory_Expires()
{
Database.PocoData.SlidingExpirationSeconds = 2;
var managedCache = new Database.ManagedCache();
int id1, id2, id3;
string alias;
CreateStuff(out id1, out id2, out id3, out alias);
QueryStuff(id1, id2, id3, alias);
var count1 = managedCache.GetCache().GetCount();
Console.WriteLine("Keys = " + count1);
Assert.Greater(count1, 0);
Thread.Sleep(10000);
var count2 = managedCache.GetCache().GetCount();
Console.WriteLine("Keys = " + count2);
Assert.Less(count2, count1);
}
private void QueryStuff(int id1, int id2, int id3, string alias1)
{
var contentService = ServiceContext.ContentService;
contentService.CountDescendants(id3);
contentService.CountChildren(id3);
contentService.Count(contentTypeAlias: alias1);
contentService.Count();
contentService.GetById(Guid.NewGuid());
contentService.GetByLevel(2);
contentService.GetChildren(id1);
contentService.GetDescendants(id2);
contentService.GetVersions(id3);
contentService.GetRootContent();
contentService.GetContentForExpiration();
contentService.GetContentForRelease();
contentService.GetContentInRecycleBin();
((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(-1))
{
Id = id1,
Path = "-1," + id1
});
contentService.GetByVersion(Guid.NewGuid());
}
private void CreateStuff(out int id1, out int id2, out int id3, out string alias)
{
var contentService = ServiceContext.ContentService;
var ctAlias = "umbTextpage" + Guid.NewGuid().ToString("N");
alias = ctAlias;
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
}
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType(ctAlias, "test Doc Type");
contentTypeService.Save(contentType);
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
}
var parent = contentService.CreateContentWithIdentity("Test", -1, ctAlias, 0);
id1 = parent.Id;
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", parent, ctAlias);
}
IContent current = parent;
for (int i = 0; i < 20; i++)
{
current = contentService.CreateContentWithIdentity("Test", current, ctAlias);
}
contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory" + Guid.NewGuid().ToString("N"), "Mandatory Doc Type", true);
contentType.PropertyGroups.First().PropertyTypes.Add(
new PropertyType(Guid.NewGuid(), DataTypeDatabaseType.Ntext)
{
Alias = "tags",
DataTypeDefinitionId = 1041
});
contentTypeService.Save(contentType);
var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1);
contentService.Publish(content1);
id2 = content1.Id;
var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1);
contentService.Publish(content2);
id3 = content2.Id;
contentService.MoveToRecycleBin(content1);
}
}
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture]
public class PetaPocoExtensionsTest : BaseDatabaseFactoryTest
@@ -21,7 +21,7 @@ namespace Umbraco.Tests.Persistence.Querying
.InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]")
.InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]")
.InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972"));
var sql = new Sql();
sql.Select("*")
@@ -36,6 +36,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -50,8 +56,8 @@ namespace Umbraco.Tests.Persistence.Querying
.InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]")
.InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]")
.InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'")
.Where("[umbracoNode].[id] = 1050");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972"))
.Where("[umbracoNode].[id] = @0", 1050);
var sql = new Sql();
sql.Select("*")
@@ -67,6 +73,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -82,9 +94,9 @@ namespace Umbraco.Tests.Persistence.Querying
.InnerJoin("[cmsContentVersion]").On("[cmsDocument].[versionId] = [cmsContentVersion].[VersionId]")
.InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]")
.InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'c66ba18e-eaf3-4cff-8a22-41b16d66a972'")
.Where("[umbracoNode].[id] = 1050")
.Where("[cmsContentVersion].[VersionId] = '2b543516-a944-4ee6-88c6-8813da7aaa07'")
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972"))
.Where("[umbracoNode].[id] = @0", 1050)
.Where("[cmsContentVersion].[VersionId] = @0", new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07"))
.OrderBy("[cmsContentVersion].[VersionDate] DESC");
var sql = new Sql();
@@ -102,6 +114,11 @@ namespace Umbraco.Tests.Persistence.Querying
.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -116,8 +133,8 @@ namespace Umbraco.Tests.Persistence.Querying
expected.Select("*");
expected.From("[cmsPropertyData]");
expected.InnerJoin("[cmsPropertyType]").On("[cmsPropertyData].[propertytypeid] = [cmsPropertyType].[id]");
expected.Where("[cmsPropertyData].[contentNodeId] = 1050");
expected.Where("[cmsPropertyData].[versionId] = '2b543516-a944-4ee6-88c6-8813da7aaa07'");
expected.Where("[cmsPropertyData].[contentNodeId] = @0", 1050);
expected.Where("[cmsPropertyData].[versionId] = @0", new Guid("2b543516-a944-4ee6-88c6-8813da7aaa07"));
var sql = new Sql();
sql.Select("*")
@@ -128,6 +145,11 @@ namespace Umbraco.Tests.Persistence.Querying
.Where<PropertyDataDto>(x => x.VersionId == versionId);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -22,8 +22,8 @@ namespace Umbraco.Tests.Persistence.Querying
.On("[cmsContentType].[nodeId] = [cmsDocumentType].[contentTypeNodeId]")
.InnerJoin("[umbracoNode]")
.On("[cmsContentType].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'a2cb7800-f571-4787-9638-bc48539a0efb'")
.Where("[cmsDocumentType].[IsDefault] = 1");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("a2cb7800-f571-4787-9638-bc48539a0efb"))
.Where("[cmsDocumentType].[IsDefault] = @0", true);
var sql = new Sql();
sql.Select("*")
@@ -37,6 +37,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -52,9 +58,9 @@ namespace Umbraco.Tests.Persistence.Querying
.On("[cmsContentType].[nodeId] = [cmsDocumentType].[contentTypeNodeId]")
.InnerJoin("[umbracoNode]")
.On("[cmsContentType].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'a2cb7800-f571-4787-9638-bc48539a0efb'")
.Where("[cmsDocumentType].[IsDefault] = 1")
.Where("[umbracoNode].[id] = 1050");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("a2cb7800-f571-4787-9638-bc48539a0efb"))
.Where("[cmsDocumentType].[IsDefault] = @0", true)
.Where("[umbracoNode].[id] = @0", 1050);
var sql = new Sql();
sql.Select("*")
@@ -64,11 +70,17 @@ namespace Umbraco.Tests.Persistence.Querying
.InnerJoin<NodeDto>()
.On<ContentTypeDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectType)
.Where<DocumentTypeDto>(x => x.IsDefault == true)
.Where<DocumentTypeDto>(x => x.IsDefault)
.Where<NodeDto>(x => x.NodeId == 1050);
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -100,7 +112,7 @@ namespace Umbraco.Tests.Persistence.Querying
var expected = new Sql();
expected.Select("*")
.From("[cmsContentTypeAllowedContentType]")
.Where("[cmsContentTypeAllowedContentType].[Id] = 1050");
.Where("[cmsContentTypeAllowedContentType].[Id] = @0", 1050);
var sql = new Sql();
sql.Select("*")
@@ -109,6 +121,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
@@ -120,7 +138,7 @@ namespace Umbraco.Tests.Persistence.Querying
.From("[cmsPropertyTypeGroup]")
.RightJoin("[cmsPropertyType]").On("[cmsPropertyTypeGroup].[id] = [cmsPropertyType].[propertyTypeGroupId]")
.InnerJoin("[cmsDataType]").On("[cmsPropertyType].[dataTypeId] = [cmsDataType].[nodeId]")
.Where("[cmsPropertyType].[contentTypeId] = 1050");
.Where("[cmsPropertyType].[contentTypeId] = @0", 1050);
var sql = new Sql();
sql.Select("*")
@@ -133,6 +151,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
}
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.Persistence.Querying
expected.Select("*")
.From("[cmsDataType]")
.InnerJoin("[umbracoNode]").On("[cmsDataType].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = '30a2a501-1978-4ddb-a57b-f7efed43ba3c'");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("30a2a501-1978-4ddb-a57b-f7efed43ba3c"));
var sql = new Sql();
sql.Select("*")
@@ -30,6 +30,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
}
@@ -23,7 +23,8 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("upper([umbracoNode].[path]) like '-1%'", result);
Assert.AreEqual("upper([umbracoNode].[path]) LIKE upper(@0)", result);
Assert.AreEqual("-1%", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
@@ -36,7 +37,8 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("[umbracoNode].[parentID] = -1", result);
Assert.AreEqual("[umbracoNode].[parentID] = @0", result);
Assert.AreEqual(-1, modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
@@ -48,7 +50,8 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("[umbracoUser].[userLogin] = 'hello@@world.com'", result);
Assert.AreEqual("[umbracoUser].[userLogin] = @0", result);
Assert.AreEqual("hello@world.com", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
@@ -60,7 +63,8 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("upper([umbracoUser].[userLogin]) = 'HELLO@@WORLD.COM'", result);
Assert.AreEqual("upper([umbracoUser].[userLogin]) = upper(@0)", result);
Assert.AreEqual("hello@world.com", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
@@ -75,7 +79,9 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Model to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("upper(`umbracoUser`.`userLogin`) = 'MYDOMAIN\\\\MYUSER'", result);
Assert.AreEqual("upper(`umbracoUser`.`userLogin`) = upper(@0)", result);
Assert.AreEqual("mydomain\\myuser", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
[Test]
@@ -90,7 +96,8 @@ namespace Umbraco.Tests.Persistence.Querying
Console.WriteLine("Poco to Sql ExpressionHelper: \n" + result);
Assert.AreEqual("upper(`umbracoUser`.`userLogin`) like 'MYDOMAIN\\\\MYUSER%'", result);
Assert.AreEqual("upper(`umbracoUser`.`userLogin`) LIKE upper(@0)", result);
Assert.AreEqual("mydomain\\myuser%", modelToSqlExpressionHelper.GetSqlParameters()[0]);
}
}
@@ -20,7 +20,7 @@ namespace Umbraco.Tests.Persistence.Querying
.From("[cmsContentVersion]")
.InnerJoin("[cmsContent]").On("[cmsContentVersion].[ContentId] = [cmsContent].[nodeId]")
.InnerJoin("[umbracoNode]").On("[cmsContent].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = 'b796f64c-1f99-4ffb-b886-4bf4bc011a9c'");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("b796f64c-1f99-4ffb-b886-4bf4bc011a9c"));
var sql = new Sql();
sql.Select("*")
@@ -33,6 +33,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
}
@@ -19,7 +19,7 @@ namespace Umbraco.Tests.Persistence.Querying
expected.Select("*")
.From("[cmsContentType]")
.InnerJoin("[umbracoNode]").On("[cmsContentType].[nodeId] = [umbracoNode].[id]")
.Where("[umbracoNode].[nodeObjectType] = '4ea4382b-2f5a-4c2b-9587-ae9b3cf3602e'");
.Where("[umbracoNode].[nodeObjectType] = @0", new Guid("4ea4382b-2f5a-4c2b-9587-ae9b3cf3602e"));
var sql = new Sql();
sql.Select("*")
@@ -30,6 +30,12 @@ namespace Umbraco.Tests.Persistence.Querying
Assert.That(sql.SQL, Is.EqualTo(expected.SQL));
Assert.AreEqual(expected.Arguments.Length, sql.Arguments.Length);
for (int i = 0; i < expected.Arguments.Length; i++)
{
Assert.AreEqual(expected.Arguments[i], sql.Arguments[i]);
}
Console.WriteLine(sql.SQL);
}
}
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -6,12 +7,141 @@ using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Tests.TestHelpers;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Tests.Persistence.Querying
{
[TestFixture]
public class PetaPocoSqlTests : BaseUsingSqlCeSyntax
{
//x =>
[Test]
public void Where_Clause_With_Starts_With_Additional_Parameters()
{
var content = new NodeDto() { NodeId = 123, Path = "-1,123" };
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Path.SqlStartsWith(content.Path, TextColumnType.NVarchar));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[path]) LIKE upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(content.Path + "%", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Starts_With_By_Variable()
{
var content = new NodeDto() {NodeId = 123, Path = "-1,123"};
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Path.StartsWith(content.Path) && x.NodeId != content.NodeId);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[path]) LIKE upper(@0) AND [umbracoNode].[id] <> @1)", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(content.Path + "%", sql.Arguments[0]);
Assert.AreEqual(content.NodeId, sql.Arguments[1]);
}
[Test]
public void Where_Clause_With_Not_Starts_With()
{
var level = 1;
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Level == level && !x.Path.StartsWith("-20"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[level] = @0 AND NOT (upper([umbracoNode].[path]) LIKE upper(@1)))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(2, sql.Arguments.Length);
Assert.AreEqual(level, sql.Arguments[0]);
Assert.AreEqual("-20%", sql.Arguments[1]);
}
[Test]
public void Where_Clause_With_Equals_Clause()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Text.Equals("Hello@world.com"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[text]) = upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("Hello@world.com", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_False_Boolean()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => !x.Trashed);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (NOT ([umbracoNode].[trashed] = @0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(true, sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Boolean()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Trashed);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[trashed] = @0)", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(true, sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_ToUpper()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Text.ToUpper() == "hello".ToUpper());
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[text]) = upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("hello", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_ToString()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Text == 1.ToString());
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[text] = @0)", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("1", sql.Arguments[0]);
}
[Test]
public void Where_Clause_With_Wildcard()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Text.StartsWith("D"));
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (upper([umbracoNode].[text]) LIKE upper(@0))", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual("D%", sql.Arguments[0]);
}
[Test]
public void Where_Clause_Single_Constant()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.NodeId == 2);
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[id] = @0)", sql.SQL.Replace("\n", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(2, sql.Arguments[0]);
}
[Test]
public void Where_Clause_And_Constant()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.NodeId != 2 && x.NodeId != 3);
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[1]);
}
[Test]
public void Where_Clause_Or_Constant()
{
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(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]
public void Can_Select_From_With_Type()
@@ -78,7 +208,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<ContentDto>().Where<ContentDto>(x => x.NodeId == 1045);
@@ -94,8 +224,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("*")
@@ -12,21 +12,7 @@ namespace Umbraco.Tests.Persistence.Querying
[TestFixture]
public class QueryBuilderTests : BaseUsingSqlCeSyntax
{
[Test]
public void Dates_Formatted_Properly()
{
var sql = new Sql();
sql.Select("*").From<DocumentDto>();
var dt = new DateTime(2013, 11, 21, 13, 25, 55);
var query = Query<IContent>.Builder.Where(x => x.ExpireDate <= dt);
var translator = new SqlTranslator<IContent>(sql, query);
var result = translator.Translate();
Assert.IsTrue(result.SQL.Contains("[expireDate] <= '2013-11-21 13:25:55'"));
}
[Test]
public void Can_Build_StartsWith_Query_For_IContent()
@@ -43,11 +29,15 @@ namespace Umbraco.Tests.Persistence.Querying
var result = translator.Translate();
var strResult = result.SQL;
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (upper([umbracoNode].[path]) like '-1%')";
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE (upper([umbracoNode].[path]) LIKE upper(@0))";
// Assert
Assert.That(strResult, Is.Not.Empty);
Assert.That(strResult, Is.EqualTo(expectedResult));
Assert.AreEqual(1, result.Arguments.Length);
Assert.AreEqual("-1%", sql.Arguments[0]);
Console.WriteLine(strResult);
}
@@ -66,11 +56,15 @@ namespace Umbraco.Tests.Persistence.Querying
var result = translator.Translate();
var strResult = result.SQL;
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([umbracoNode].[parentID] = -1)";
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([umbracoNode].[parentID] = @0)";
// Assert
Assert.That(strResult, Is.Not.Empty);
Assert.That(strResult, Is.EqualTo(expectedResult));
Assert.AreEqual(1, result.Arguments.Length);
Assert.AreEqual(-1, sql.Arguments[0]);
Console.WriteLine(strResult);
}
@@ -89,11 +83,14 @@ namespace Umbraco.Tests.Persistence.Querying
var result = translator.Translate();
var strResult = result.SQL;
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([cmsContentType].[alias] = 'umbTextpage')";
string expectedResult = "SELECT *\nFROM umbracoNode\nWHERE ([cmsContentType].[alias] = @0)";
// Assert
Assert.That(strResult, Is.Not.Empty);
Assert.That(strResult, Is.EqualTo(expectedResult));
Assert.AreEqual(1, result.Arguments.Length);
Assert.AreEqual("umbTextpage", sql.Arguments[0]);
Console.WriteLine(strResult);
}
@@ -38,7 +38,11 @@ namespace Umbraco.Tests.Persistence.SyntaxProvider
FROM [cmsContentXml]
INNER JOIN [umbracoNode]
ON [cmsContentXml].[nodeId] = [umbracoNode].[id]
WHERE ([umbracoNode].[nodeObjectType] = 'b796f64c-1f99-4ffb-b886-4bf4bc011a9c')) x)".Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "), sql.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "));
WHERE ([umbracoNode].[nodeObjectType] = @0)) x)".Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "),
sql.SQL.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "));
Assert.AreEqual(1, sql.Arguments.Length);
Assert.AreEqual(mediaObjectType, sql.Arguments[0]);
}
[NUnit.Framework.Ignore("This doesn't actually test anything")]
@@ -76,6 +76,30 @@ namespace Umbraco.Tests.Services
Assert.That(entities.Any(x => x.Trashed), Is.False);
}
[Test]
public void EntityService_Can_Get_Children_By_ParentId()
{
var service = ServiceContext.EntityService;
var entities = service.GetChildren(folderId);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(3));
Assert.That(entities.Any(x => x.Trashed), Is.False);
}
[Test]
public void EntityService_Can_Get_Descendants_By_ParentId()
{
var service = ServiceContext.EntityService;
var entities = service.GetDescendents(folderId);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
Assert.That(entities.Any(x => x.Trashed), Is.False);
}
[Test]
public void EntityService_Throws_When_Getting_All_With_Invalid_Type()
{
@@ -129,7 +153,7 @@ namespace Umbraco.Tests.Services
var entities = service.GetAll(UmbracoObjectTypes.Media);
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(3));
Assert.That(entities.Count(), Is.EqualTo(5));
//Assert.That(entities.Any(x => ((UmbracoEntity)x).UmbracoFile != string.Empty), Is.True);
Assert.That(
entities.Any(
@@ -140,6 +164,8 @@ namespace Umbraco.Tests.Services
private static bool _isSetup = false;
private int folderId;
public override void CreateTestData()
{
if (_isSetup == false)
@@ -150,8 +176,9 @@ namespace Umbraco.Tests.Services
//Create and Save folder-Media -> 1050
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
ServiceContext.MediaService.Save(folder, 0);
folderId = folder.Id;
//Create and Save image-Media -> 1051
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
@@ -162,6 +189,12 @@ namespace Umbraco.Tests.Services
var fileMediaType = ServiceContext.ContentTypeService.GetMediaType(1033);
var file = MockedMedia.CreateMediaFile(fileMediaType, folder.Id);
ServiceContext.MediaService.Save(file, 0);
var subfolder = MockedMedia.CreateMediaFolder(folderMediaType, folder.Id);
ServiceContext.MediaService.Save(subfolder, 0);
var subfolder2 = MockedMedia.CreateMediaFolder(folderMediaType, subfolder.Id);
ServiceContext.MediaService.Save(subfolder2, 0);
}
}
@@ -93,15 +93,20 @@ namespace Umbraco.Tests.Services
ServiceContext.MemberTypeService.Save(memberType);
IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
ServiceContext.MemberService.Save(member);
//need to test with '@' symbol in the lookup
IMember member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2@test.com");
ServiceContext.MemberService.Save(member2);
ServiceContext.MemberService.AddRole("MyTestRole1");
ServiceContext.MemberService.AddRole("MyTestRole2");
ServiceContext.MemberService.AddRole("MyTestRole3");
ServiceContext.MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" });
ServiceContext.MemberService.AssignRoles(new[] { member.Id, member2.Id }, new[] { "MyTestRole1", "MyTestRole2" });
var memberRoles = ServiceContext.MemberService.GetAllRoles("test");
Assert.AreEqual(2, memberRoles.Count());
var memberRoles2 = ServiceContext.MemberService.GetAllRoles("test2@test.com");
Assert.AreEqual(2, memberRoles2.Count());
}
[Test]
@@ -324,9 +329,12 @@ namespace Umbraco.Tests.Services
ServiceContext.MemberTypeService.Save(memberType);
IMember member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
ServiceContext.MemberService.Save(member);
IMember member2 = MockedMember.CreateSimpleMember(memberType, "test", "test2@test.com", "pass", "test2@test.com");
ServiceContext.MemberService.Save(member2);
Assert.IsTrue(ServiceContext.MemberService.Exists("test"));
Assert.IsFalse(ServiceContext.MemberService.Exists("notFound"));
Assert.IsTrue(ServiceContext.MemberService.Exists("test2@test.com"));
}
[Test]
@@ -127,9 +127,10 @@ namespace Umbraco.Tests.Services
var userType = MockedUserType.CreateUserType();
ServiceContext.UserService.SaveUserType(userType);
var user = ServiceContext.UserService.CreateUserWithIdentity("JohnDoe", "john@umbraco.io", userType);
var user2 = ServiceContext.UserService.CreateUserWithIdentity("john2@umbraco.io", "john2@umbraco.io", userType);
Assert.IsTrue(ServiceContext.UserService.Exists("JohnDoe"));
Assert.IsFalse(ServiceContext.UserService.Exists("notFound"));
Assert.IsTrue(ServiceContext.UserService.Exists("john2@umbraco.io"));
}
[Test]
+2 -2
View File
@@ -2675,9 +2675,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>6220</DevelopmentServerPort>
<DevelopmentServerPort>6230</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:6220</IISUrl>
<IISUrl>http://localhost:6240</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -123,8 +123,8 @@ namespace UmbracoExamine.DataServices
{
try
{
var result = _applicationContext.DatabaseContext.Database.Fetch<PropertyAliasDto>("select distinct alias from cmsPropertyType order by alias");
return result.Select(r => r.Alias).ToList();
var result = _applicationContext.DatabaseContext.Database.Fetch<string>("select distinct alias from cmsPropertyType order by alias");
return result;
}
catch (Exception ex)
{