This commit is contained in:
grzegorz.russek
2016-02-04 08:52:44 +00:00
parent 9e40c4e20b
commit f5b4834fd5
5 changed files with 1350 additions and 1174 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -44,7 +44,6 @@ namespace DynamORM.Builders.Implementation
/// <summary>Implementation of dynamic select query builder.</summary> /// <summary>Implementation of dynamic select query builder.</summary>
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
{ {
private int? _top = null;
private int? _limit = null; private int? _limit = null;
private int? _offset = null; private int? _offset = null;
private bool _distinct = false; private bool _distinct = false;
@@ -83,17 +82,42 @@ namespace DynamORM.Builders.Implementation
/// <returns>The text to execute against the underlying database.</returns> /// <returns>The text to execute against the underlying database.</returns>
public override string CommandText() public override string CommandText()
{ {
bool lused = false;
bool oused = false;
StringBuilder sb = new StringBuilder("SELECT"); StringBuilder sb = new StringBuilder("SELECT");
if (_distinct) sb.AppendFormat(" DISTINCT"); if (_distinct) sb.AppendFormat(" DISTINCT");
if (_top.HasValue) sb.AppendFormat(" TOP {0}", _top);
if (_limit.HasValue)
{
if ((Database.Options & DynamicDatabaseOptions.SupportTop) == DynamicDatabaseOptions.SupportTop)
{
sb.AppendFormat(" TOP {0}", _limit);
lused = true;
}
else if ((Database.Options & DynamicDatabaseOptions.SupportFirstSkip) == DynamicDatabaseOptions.SupportFirstSkip)
{
sb.AppendFormat(" FIRST {0}", _limit);
lused = true;
}
}
if (_offset.HasValue && (Database.Options & DynamicDatabaseOptions.SupportFirstSkip) == DynamicDatabaseOptions.SupportFirstSkip)
{
sb.AppendFormat(" SKIP {0}", _offset);
oused = true;
}
if (_select != null) sb.AppendFormat(" {0}", _select); else sb.Append(" *"); if (_select != null) sb.AppendFormat(" {0}", _select); else sb.Append(" *");
if (_from != null) sb.AppendFormat(" FROM {0}", _from); if (_from != null) sb.AppendFormat(" FROM {0}", _from);
if (_join != null) sb.AppendFormat(" {0}", _join); if (_join != null) sb.AppendFormat(" {0}", _join);
if (WhereCondition != null) sb.AppendFormat(" WHERE {0}", WhereCondition); if (WhereCondition != null) sb.AppendFormat(" WHERE {0}", WhereCondition);
if (_groupby != null) sb.AppendFormat(" GROUP BY {0}", _groupby); if (_groupby != null) sb.AppendFormat(" GROUP BY {0}", _groupby);
if (_orderby != null) sb.AppendFormat(" ORDER BY {0}", _orderby); if (_orderby != null) sb.AppendFormat(" ORDER BY {0}", _orderby);
if (_limit.HasValue) sb.AppendFormat(" LIMIT {0}", _limit); if (_limit.HasValue && !lused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
if (_offset.HasValue) sb.AppendFormat(" OFFSET {0}", _offset); sb.AppendFormat(" LIMIT {0}", _limit);
if (_offset.HasValue && !oused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
sb.AppendFormat(" OFFSET {0}", _offset);
return sb.ToString(); return sb.ToString();
} }
@@ -1105,11 +1129,7 @@ namespace DynamORM.Builders.Implementation
/// <returns>Builder instance.</returns> /// <returns>Builder instance.</returns>
public virtual IDynamicSelectQueryBuilder Top(int? top) public virtual IDynamicSelectQueryBuilder Top(int? top)
{ {
if ((Database.Options & DynamicDatabaseOptions.SupportTop) != DynamicDatabaseOptions.SupportTop) return Limit(top);
throw new NotSupportedException("Database doesn't support TOP clause.");
_top = top;
return this;
} }
/// <summary>Set top if database support it.</summary> /// <summary>Set top if database support it.</summary>
@@ -1117,7 +1137,9 @@ namespace DynamORM.Builders.Implementation
/// <returns>Builder instance.</returns> /// <returns>Builder instance.</returns>
public virtual IDynamicSelectQueryBuilder Limit(int? limit) public virtual IDynamicSelectQueryBuilder Limit(int? limit)
{ {
if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset) if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset &&
(Database.Options & DynamicDatabaseOptions.SupportFirstSkip) != DynamicDatabaseOptions.SupportFirstSkip &&
(Database.Options & DynamicDatabaseOptions.SupportTop) != DynamicDatabaseOptions.SupportTop)
throw new NotSupportedException("Database doesn't support LIMIT clause."); throw new NotSupportedException("Database doesn't support LIMIT clause.");
_limit = limit; _limit = limit;
@@ -1129,7 +1151,8 @@ namespace DynamORM.Builders.Implementation
/// <returns>Builder instance.</returns> /// <returns>Builder instance.</returns>
public virtual IDynamicSelectQueryBuilder Offset(int? offset) public virtual IDynamicSelectQueryBuilder Offset(int? offset)
{ {
if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset) if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset &&
(Database.Options & DynamicDatabaseOptions.SupportFirstSkip) != DynamicDatabaseOptions.SupportFirstSkip)
throw new NotSupportedException("Database doesn't support OFFSET clause."); throw new NotSupportedException("Database doesn't support OFFSET clause.");
_offset = offset; _offset = offset;

View File

@@ -50,11 +50,14 @@ namespace DynamORM
/// <summary>Database supports limit offset syntax (SELECT ... FROM ... LIMIT x OFFSET y).</summary> /// <summary>Database supports limit offset syntax (SELECT ... FROM ... LIMIT x OFFSET y).</summary>
SupportLimitOffset = 0x00000040, SupportLimitOffset = 0x00000040,
/// <summary>Database supports limit offset syntax (SELECT FIRST x SKIP y ... FROM ...).</summary>
SupportFirstSkip = 0x00000020,
/// <summary>Database support standard schema.</summary> /// <summary>Database support standard schema.</summary>
SupportSchema = 0x00000010, SupportSchema = 0x00000010,
/// <summary>Database support stored procedures (EXEC procedure ...).</summary> /// <summary>Database support stored procedures (EXEC procedure ...).</summary>
SupportStoredProcedures = 0x00000020, SupportStoredProcedures = 0x00000100,
/// <summary>Debug option allowing to enable command dumps by default.</summary> /// <summary>Debug option allowing to enable command dumps by default.</summary>
DumpCommands = 0x01000000, DumpCommands = 0x01000000,

View File

@@ -1283,7 +1283,7 @@ namespace DynamORM
/// <returns>Returns <c>true</c> if it does.</returns> /// <returns>Returns <c>true</c> if it does.</returns>
public static bool IsGenericEnumerable(this Type type) public static bool IsGenericEnumerable(this Type type)
{ {
return type.IsGenericType && type.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); return type.IsGenericType && type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
} }
/// <summary>Check if type implements IEnumerable&lt;&gt; interface.</summary> /// <summary>Check if type implements IEnumerable&lt;&gt; interface.</summary>

View File

@@ -27,7 +27,9 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@@ -45,6 +47,9 @@ namespace DynamORM.Mapper
public int Ordinal { get; set; } public int Ordinal { get; set; }
} }
private Type _arrayType;
private bool _genericEnumerable;
/// <summary>Gets the type of property.</summary> /// <summary>Gets the type of property.</summary>
public Type Type { get; private set; } public Type Type { get; private set; }
@@ -63,6 +68,9 @@ namespace DynamORM.Mapper
/// <summary>Gets a value indicating whether this <see cref="DynamicPropertyInvoker"/> is ignored in some cases.</summary> /// <summary>Gets a value indicating whether this <see cref="DynamicPropertyInvoker"/> is ignored in some cases.</summary>
public bool Ignore { get; private set; } public bool Ignore { get; private set; }
/// <summary>Gets a value indicating whether this instance hold data contract type.</summary>
public bool IsDataContract { get; private set; }
/// <summary>Initializes a new instance of the <see cref="DynamicPropertyInvoker" /> class.</summary> /// <summary>Initializes a new instance of the <see cref="DynamicPropertyInvoker" /> class.</summary>
/// <param name="property">Property info to be invoked in the future.</param> /// <param name="property">Property info to be invoked in the future.</param>
/// <param name="attr">Column attribute if exist.</param> /// <param name="attr">Column attribute if exist.</param>
@@ -75,6 +83,20 @@ namespace DynamORM.Mapper
Ignore = ignore != null && ignore.Length > 0; Ignore = ignore != null && ignore.Length > 0;
_arrayType = Type.IsArray ? Type.GetElementType() :
Type.IsGenericEnumerable() ? Type.GetGenericArguments().First() :
Type;
_genericEnumerable = Type.IsGenericEnumerable();
IsDataContract = _arrayType.GetCustomAttributes(false).Any(x => x.GetType().Name == "DataContractAttribute");
if (_arrayType.IsArray)
throw new InvalidOperationException("Jagged arrays are not supported");
if (_arrayType.IsGenericEnumerable())
throw new InvalidOperationException("Enumerables of enumerables are not supported");
Column = attr; Column = attr;
if (property.CanRead) if (property.CanRead)
@@ -121,43 +143,65 @@ namespace DynamORM.Mapper
/// <param name="val">The value.</param> /// <param name="val">The value.</param>
public void Set(object dest, object val) public void Set(object dest, object val)
{ {
Type type = Nullable.GetUnderlyingType(Type) ?? Type; object value = null;
bool nullable = Type.IsGenericType && Type.GetGenericTypeDefinition() == typeof(Nullable<>);
try try
{ {
if (val == null && type.IsValueType) if (Type.IsArray || _genericEnumerable)
{ {
if (nullable) var lst = (val as IEnumerable<object>).Select(x => GetElementVal(_arrayType, x)).ToList();
Setter(dest, null);
else value = Array.CreateInstance(_arrayType, lst.Count);
Setter(dest, Activator.CreateInstance(Type));
} int i = 0;
else if ((val == null && !type.IsValueType) || (val != null && type == val.GetType())) foreach (var e in lst)
Setter(dest, val); ((Array)value).SetValue(e, i++);
else if (type.IsEnum && val.GetType().IsValueType)
Setter(dest, Enum.ToObject(type, val));
else if (type.IsEnum)
Setter(dest, Enum.Parse(type, val.ToString()));
else if (Type == typeof(string) && val.GetType() == typeof(Guid))
Setter(dest, val.ToString());
else if (Type == typeof(Guid) && val.GetType() == typeof(string))
{
Guid g;
Setter(dest, Guid.TryParse((string)val, out g) ? g : Guid.Empty);
} }
else else
Setter(dest, Convert.ChangeType(val, type)); value = GetElementVal(Type, val);
Setter(dest, value);
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new InvalidCastException( throw new InvalidCastException(
string.Format("Error trying to convert value '{0}' of type '{1}' to value of type '{2}{3}' in object of type '{4}'", string.Format("Error trying to convert value '{0}' of type '{1}' to value of type '{2}' in object of type '{3}'",
val.ToString(), val.GetType(), type.FullName, nullable ? "(NULLABLE)" : string.Empty, dest.GetType().FullName), (val ?? string.Empty).ToString(), val.GetType(), Type.FullName, dest.GetType().FullName),
ex); ex);
} }
} }
private object GetElementVal(System.Type etype, object val)
{
bool nullable = etype.IsGenericType && etype.GetGenericTypeDefinition() == typeof(Nullable<>);
Type type = Nullable.GetUnderlyingType(etype) ?? etype;
if (val == null && type.IsValueType)
{
if (nullable)
return null;
else
return Activator.CreateInstance(Type);
}
else if ((val == null && !type.IsValueType) || (val != null && type == val.GetType()))
return val;
else if (type.IsEnum && val.GetType().IsValueType)
return Enum.ToObject(type, val);
else if (type.IsEnum)
return Enum.Parse(type, val.ToString());
else if (Type == typeof(string) && val.GetType() == typeof(Guid))
return val.ToString();
else if (Type == typeof(Guid) && val.GetType() == typeof(string))
{
Guid g;
return Guid.TryParse((string)val, out g) ? g : Guid.Empty;
}
else if (IsDataContract)
return val.Map(type);
else
return Convert.ChangeType(val, type);
}
#region Type command cache #region Type command cache
internal ParameterSpec InsertCommandParameter { get; set; } internal ParameterSpec InsertCommandParameter { get; set; }