Added HAVING clause
This commit is contained in:
@@ -6674,6 +6674,47 @@ namespace DynamORM
|
||||
|
||||
#endregion GroupBy
|
||||
|
||||
#region Having
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Having( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Having(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(DynamicColumn column);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(object conditions, bool schema = false);
|
||||
|
||||
#endregion Having
|
||||
|
||||
#region OrderBy
|
||||
|
||||
/// <summary>
|
||||
@@ -6857,6 +6898,211 @@ namespace DynamORM
|
||||
|
||||
namespace Extensions
|
||||
{
|
||||
internal static class DynamicHavingQueryExtensions
|
||||
{
|
||||
#region Where
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(false, false, func);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, bool addBeginBrace, bool addEndBrace, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(func))
|
||||
{
|
||||
string condition = null;
|
||||
bool and = true;
|
||||
|
||||
object result = parser.Result;
|
||||
if (result is string)
|
||||
{
|
||||
condition = (string)result;
|
||||
|
||||
if (condition.ToUpper().IndexOf("OR") == 0)
|
||||
{
|
||||
and = false;
|
||||
condition = condition.Substring(3);
|
||||
}
|
||||
else if (condition.ToUpper().IndexOf("AND") == 0)
|
||||
condition = condition.Substring(4);
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
return builder.InternalHaving(result);
|
||||
else
|
||||
{
|
||||
// Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods...
|
||||
if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument)
|
||||
{
|
||||
DynamicParser.Node.Method node = (DynamicParser.Node.Method)result;
|
||||
string name = node.Name.ToUpper();
|
||||
if (name == "AND" || name == "OR")
|
||||
{
|
||||
object[] args = ((DynamicParser.Node.Method)node).Arguments;
|
||||
if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name));
|
||||
if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch()));
|
||||
|
||||
and = name == "AND" ? true : false;
|
||||
result = args[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Just parsing the contents now...
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.HavingOpenBracketsCount++;
|
||||
if (addEndBrace) builder.HavingOpenBracketsCount--;
|
||||
|
||||
if (builder.HavingCondition == null)
|
||||
builder.HavingCondition = string.Format("{0}{1}{2}",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
else
|
||||
builder.HavingCondition = string.Format("{0} {1} {2}{3}{4}", builder.HavingCondition, and ? "AND" : "OR",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
bool virt = builder.VirtualMode;
|
||||
if (column.VirtualColumn.HasValue)
|
||||
builder.VirtualMode = column.VirtualColumn.Value;
|
||||
|
||||
Action<IParameter> modParam = (p) =>
|
||||
{
|
||||
if (column.Schema.HasValue)
|
||||
p.Schema = column.Schema;
|
||||
|
||||
if (!p.Schema.HasValue)
|
||||
p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName);
|
||||
};
|
||||
|
||||
builder.CreateTemporaryParameterAction(modParam);
|
||||
|
||||
// It's kind of uglu, but... well it works.
|
||||
if (column.Or)
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||
}
|
||||
else
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||
}
|
||||
|
||||
builder.OnCreateTemporaryParameter.Remove(modParam);
|
||||
builder.VirtualMode = virt;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return builder.InternalHaving(v);
|
||||
}
|
||||
else if (value is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)value)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
return builder.InternalHaving(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Operator = op,
|
||||
Value = value
|
||||
});
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(column, DynamicColumn.CompareOperator.Eq, value);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return builder.InternalHaving((DynamicColumn)conditions);
|
||||
else if (conditions is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)conditions)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = conditions.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty);
|
||||
|
||||
foreach (KeyValuePair<string, object> condition in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table")
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key;
|
||||
|
||||
DynamicSchemaColumn? col = null;
|
||||
|
||||
// This should be used on typed queries or update/delete steatements, which usualy operate on a single table.
|
||||
if (schema)
|
||||
{
|
||||
col = builder.GetColumnFromSchema(colName, mapper, table);
|
||||
|
||||
if ((!col.HasValue || !col.Value.IsKey) &&
|
||||
(mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true)))
|
||||
continue;
|
||||
|
||||
colName = col.HasValue ? col.Value.Name : colName;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(table))
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value);
|
||||
else
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(colName)) == condition.Value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
|
||||
internal static class DynamicModifyBuilderExtensions
|
||||
{
|
||||
internal static T Table<T>(this T builder, Func<dynamic, object> func) where T : DynamicModifyBuilder
|
||||
@@ -7033,8 +7279,8 @@ namespace DynamORM
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.OpenBracketsCount++;
|
||||
if (addEndBrace) builder.OpenBracketsCount--;
|
||||
if (addBeginBrace) builder.WhereOpenBracketsCount++;
|
||||
if (addEndBrace) builder.WhereOpenBracketsCount--;
|
||||
|
||||
if (builder.WhereCondition == null)
|
||||
builder.WhereCondition = string.Format("{0}{1}{2}",
|
||||
@@ -7512,7 +7758,17 @@ namespace DynamORM
|
||||
string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
int OpenBracketsCount { get; set; }
|
||||
int WhereOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Empty interface to allow having query builder implementation use universal approach.</summary>
|
||||
internal interface IQueryWithHaving
|
||||
{
|
||||
/// <summary>Gets or sets the having condition.</summary>
|
||||
string HavingCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in having statement.</summary>
|
||||
int HavingOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
private DynamicQueryBuilder _parent = null;
|
||||
@@ -7679,7 +7935,7 @@ namespace DynamORM
|
||||
OnCreateParameter = new List<Action<IParameter, IDbDataParameter>>();
|
||||
|
||||
WhereCondition = null;
|
||||
OpenBracketsCount = 0;
|
||||
WhereOpenBracketsCount = 0;
|
||||
|
||||
Database = db;
|
||||
if (Database != null)
|
||||
@@ -7705,7 +7961,7 @@ namespace DynamORM
|
||||
public string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
public int OpenBracketsCount { get; set; }
|
||||
public int WhereOpenBracketsCount { get; set; }
|
||||
|
||||
#endregion IQueryWithWhere
|
||||
|
||||
@@ -7749,10 +8005,22 @@ namespace DynamORM
|
||||
// End not ended where statement
|
||||
if (this is IQueryWithWhere)
|
||||
{
|
||||
while (OpenBracketsCount > 0)
|
||||
while (WhereOpenBracketsCount > 0)
|
||||
{
|
||||
WhereCondition += ")";
|
||||
OpenBracketsCount--;
|
||||
WhereOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// End not ended having statement
|
||||
if (this is IQueryWithHaving)
|
||||
{
|
||||
IQueryWithHaving h = this as IQueryWithHaving;
|
||||
|
||||
while (h.HavingOpenBracketsCount > 0)
|
||||
{
|
||||
h.HavingCondition += ")";
|
||||
h.HavingOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8170,6 +8438,12 @@ namespace DynamORM
|
||||
return "COUNT(*)";
|
||||
|
||||
return string.Format("COUNT({0})", Parse(node.Arguments[0], ref columnSchema, pars: Parameters, nulls: true));
|
||||
|
||||
case "COUNT0":
|
||||
if (node.Arguments != null && node.Arguments.Length > 0)
|
||||
throw new ArgumentException("COUNT0 method doesn't expect arguments");
|
||||
|
||||
return "COUNT(0)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8407,7 +8681,7 @@ namespace DynamORM
|
||||
}
|
||||
|
||||
/// <summary>Implementation of dynamic select query builder.</summary>
|
||||
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
private int? _limit = null;
|
||||
private int? _offset = null;
|
||||
@@ -8419,6 +8693,16 @@ namespace DynamORM
|
||||
private string _groupby;
|
||||
private string _orderby;
|
||||
|
||||
#region IQueryWithHaving
|
||||
|
||||
/// <summary>Gets or sets the having condition.</summary>
|
||||
public string HavingCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in having statement.</summary>
|
||||
public int HavingOpenBracketsCount { get; set; }
|
||||
|
||||
#endregion IQueryWithHaving
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has select columns.
|
||||
/// </summary>
|
||||
@@ -8478,6 +8762,7 @@ namespace DynamORM
|
||||
if (_join != null) sb.AppendFormat(" {0}", _join);
|
||||
if (WhereCondition != null) sb.AppendFormat(" WHERE {0}", WhereCondition);
|
||||
if (_groupby != null) sb.AppendFormat(" GROUP BY {0}", _groupby);
|
||||
if (HavingCondition != null) sb.AppendFormat(" HAVING {0}", HavingCondition);
|
||||
if (_orderby != null) sb.AppendFormat(" ORDER BY {0}", _orderby);
|
||||
if (_limit.HasValue && !lused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
|
||||
sb.AppendFormat(" LIMIT {0}", _limit);
|
||||
@@ -9377,6 +9662,62 @@ namespace DynamORM
|
||||
|
||||
#endregion GroupBy
|
||||
|
||||
#region Having
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Having( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(Func<dynamic, object> func)
|
||||
{
|
||||
return this.InternalHaving(func);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(DynamicColumn column)
|
||||
{
|
||||
return this.InternalHaving(column);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
return this.InternalHaving(column, op, value);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(string column, object value)
|
||||
{
|
||||
return this.InternalHaving(column, value);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(object conditions, bool schema = false)
|
||||
{
|
||||
return this.InternalHaving(conditions, schema);
|
||||
}
|
||||
|
||||
#endregion Having
|
||||
|
||||
#region OrderBy
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
|
||||
namespace DynamORM.Tests.Select
|
||||
{
|
||||
@@ -225,6 +225,20 @@ namespace DynamORM.Tests.Select
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE (c.\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests where method with alias.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestHavingAlias()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Having(u => u.Sum(u.c.ClientsCount) > 10);
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c HAVING (Sum(c.\"ClientsCount\") > [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests complex where method with alias.
|
||||
/// </summary>
|
||||
@@ -735,6 +749,25 @@ namespace DynamORM.Tests.Select
|
||||
cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCoalesceCalculatedArgs()
|
||||
{
|
||||
IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database);
|
||||
|
||||
cmd.From(u => u.dbo.Users.As(u.c))
|
||||
.Select(u => u.Coalesce(u.c.Test1 + "_", u.c.Test2 + "_", u.c.Test3 + "_").As(u.Hash));
|
||||
|
||||
Assert.AreEqual(string.Format("SELECT Coalesce((c.\"Test1\" + [${0}]), (c.\"Test2\" + [${1}]), (c.\"Test3\" + [${2}])) AS \"Hash\" FROM \"dbo\".\"Users\" AS c",
|
||||
cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText());
|
||||
|
||||
//var c = Database.Open().CreateCommand();
|
||||
//cmd.FillCommand(c);
|
||||
//c.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests select escaped case.
|
||||
/// </summary>
|
||||
|
||||
245
DynamORM/Builders/Extensions/DynamicHavingQueryExtensions.cs
Normal file
245
DynamORM/Builders/Extensions/DynamicHavingQueryExtensions.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* DynamORM - Dynamic Object-Relational Mapping library.
|
||||
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Some of methods in this code file is based on Kerosene ORM solution
|
||||
* for parsing dynamic lambda expressions by Moisés Barba Cebeira
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Builders.Implementation;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM.Builders.Extensions
|
||||
{
|
||||
internal static class DynamicHavingQueryExtensions
|
||||
{
|
||||
#region Where
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(false, false, func);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, bool addBeginBrace, bool addEndBrace, Func<dynamic, object> func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (func == null) throw new ArgumentNullException("Array of functions cannot be null.");
|
||||
|
||||
using (DynamicParser parser = DynamicParser.Parse(func))
|
||||
{
|
||||
string condition = null;
|
||||
bool and = true;
|
||||
|
||||
object result = parser.Result;
|
||||
if (result is string)
|
||||
{
|
||||
condition = (string)result;
|
||||
|
||||
if (condition.ToUpper().IndexOf("OR") == 0)
|
||||
{
|
||||
and = false;
|
||||
condition = condition.Substring(3);
|
||||
}
|
||||
else if (condition.ToUpper().IndexOf("AND") == 0)
|
||||
condition = condition.Substring(4);
|
||||
}
|
||||
else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType)
|
||||
return builder.InternalHaving(result);
|
||||
else
|
||||
{
|
||||
// Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods...
|
||||
if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument)
|
||||
{
|
||||
DynamicParser.Node.Method node = (DynamicParser.Node.Method)result;
|
||||
string name = node.Name.ToUpper();
|
||||
if (name == "AND" || name == "OR")
|
||||
{
|
||||
object[] args = ((DynamicParser.Node.Method)node).Arguments;
|
||||
if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name));
|
||||
if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch()));
|
||||
|
||||
and = name == "AND" ? true : false;
|
||||
result = args[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Just parsing the contents now...
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.HavingOpenBracketsCount++;
|
||||
if (addEndBrace) builder.HavingOpenBracketsCount--;
|
||||
|
||||
if (builder.HavingCondition == null)
|
||||
builder.HavingCondition = string.Format("{0}{1}{2}",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
else
|
||||
builder.HavingCondition = string.Format("{0} {1} {2}{3}{4}", builder.HavingCondition, and ? "AND" : "OR",
|
||||
addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
bool virt = builder.VirtualMode;
|
||||
if (column.VirtualColumn.HasValue)
|
||||
builder.VirtualMode = column.VirtualColumn.Value;
|
||||
|
||||
Action<IParameter> modParam = (p) =>
|
||||
{
|
||||
if (column.Schema.HasValue)
|
||||
p.Schema = column.Schema;
|
||||
|
||||
if (!p.Schema.HasValue)
|
||||
p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName);
|
||||
};
|
||||
|
||||
builder.CreateTemporaryParameterAction(modParam);
|
||||
|
||||
// It's kind of uglu, but... well it works.
|
||||
if (column.Or)
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break;
|
||||
}
|
||||
else
|
||||
switch (column.Operator)
|
||||
{
|
||||
default:
|
||||
case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break;
|
||||
case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break;
|
||||
case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break;
|
||||
}
|
||||
|
||||
builder.OnCreateTemporaryParameter.Remove(modParam);
|
||||
builder.VirtualMode = virt;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (value is DynamicColumn)
|
||||
{
|
||||
DynamicColumn v = (DynamicColumn)value;
|
||||
|
||||
if (string.IsNullOrEmpty(v.ColumnName))
|
||||
v.ColumnName = column;
|
||||
|
||||
return builder.InternalHaving(v);
|
||||
}
|
||||
else if (value is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)value)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
return builder.InternalHaving(new DynamicColumn
|
||||
{
|
||||
ColumnName = column,
|
||||
Operator = op,
|
||||
Value = value
|
||||
});
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
return builder.InternalHaving(column, DynamicColumn.CompareOperator.Eq, value);
|
||||
}
|
||||
|
||||
internal static T InternalHaving<T>(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
if (conditions is DynamicColumn)
|
||||
return builder.InternalHaving((DynamicColumn)conditions);
|
||||
else if (conditions is IEnumerable<DynamicColumn>)
|
||||
{
|
||||
foreach (DynamicColumn v in (IEnumerable<DynamicColumn>)conditions)
|
||||
builder.InternalHaving(v);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
IDictionary<string, object> dict = conditions.ToDictionary();
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType());
|
||||
string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty);
|
||||
|
||||
foreach (KeyValuePair<string, object> condition in dict)
|
||||
{
|
||||
if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table")
|
||||
continue;
|
||||
|
||||
string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key;
|
||||
|
||||
DynamicSchemaColumn? col = null;
|
||||
|
||||
// This should be used on typed queries or update/delete steatements, which usualy operate on a single table.
|
||||
if (schema)
|
||||
{
|
||||
col = builder.GetColumnFromSchema(colName, mapper, table);
|
||||
|
||||
if ((!col.HasValue || !col.Value.IsKey) &&
|
||||
(mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true)))
|
||||
continue;
|
||||
|
||||
colName = col.HasValue ? col.Value.Name : colName;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(table))
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value);
|
||||
else
|
||||
builder.InternalHaving(x => x(builder.FixObjectName(colName)) == condition.Value);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
#endregion Where
|
||||
}
|
||||
}
|
||||
@@ -93,8 +93,8 @@ namespace DynamORM.Builders.Extensions
|
||||
condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition");
|
||||
}
|
||||
|
||||
if (addBeginBrace) builder.OpenBracketsCount++;
|
||||
if (addEndBrace) builder.OpenBracketsCount--;
|
||||
if (addBeginBrace) builder.WhereOpenBracketsCount++;
|
||||
if (addEndBrace) builder.WhereOpenBracketsCount--;
|
||||
|
||||
if (builder.WhereCondition == null)
|
||||
builder.WhereCondition = string.Format("{0}{1}{2}",
|
||||
|
||||
@@ -193,6 +193,47 @@ namespace DynamORM.Builders
|
||||
|
||||
#endregion GroupBy
|
||||
|
||||
#region Having
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Having( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
IDynamicSelectQueryBuilder Having(Func<dynamic, object> func);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(DynamicColumn column);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(string column, object value);
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
IDynamicSelectQueryBuilder Having(object conditions, bool schema = false);
|
||||
|
||||
#endregion Having
|
||||
|
||||
#region OrderBy
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,7 +51,17 @@ namespace DynamORM.Builders.Implementation
|
||||
string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
int OpenBracketsCount { get; set; }
|
||||
int WhereOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Empty interface to allow having query builder implementation use universal approach.</summary>
|
||||
internal interface IQueryWithHaving
|
||||
{
|
||||
/// <summary>Gets or sets the having condition.</summary>
|
||||
string HavingCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in having statement.</summary>
|
||||
int HavingOpenBracketsCount { get; set; }
|
||||
}
|
||||
|
||||
private DynamicQueryBuilder _parent = null;
|
||||
@@ -218,7 +228,7 @@ namespace DynamORM.Builders.Implementation
|
||||
OnCreateParameter = new List<Action<IParameter, IDbDataParameter>>();
|
||||
|
||||
WhereCondition = null;
|
||||
OpenBracketsCount = 0;
|
||||
WhereOpenBracketsCount = 0;
|
||||
|
||||
Database = db;
|
||||
if (Database != null)
|
||||
@@ -244,7 +254,7 @@ namespace DynamORM.Builders.Implementation
|
||||
public string WhereCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in where statement.</summary>
|
||||
public int OpenBracketsCount { get; set; }
|
||||
public int WhereOpenBracketsCount { get; set; }
|
||||
|
||||
#endregion IQueryWithWhere
|
||||
|
||||
@@ -288,10 +298,22 @@ namespace DynamORM.Builders.Implementation
|
||||
// End not ended where statement
|
||||
if (this is IQueryWithWhere)
|
||||
{
|
||||
while (OpenBracketsCount > 0)
|
||||
while (WhereOpenBracketsCount > 0)
|
||||
{
|
||||
WhereCondition += ")";
|
||||
OpenBracketsCount--;
|
||||
WhereOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// End not ended having statement
|
||||
if (this is IQueryWithHaving)
|
||||
{
|
||||
IQueryWithHaving h = this as IQueryWithHaving;
|
||||
|
||||
while (h.HavingOpenBracketsCount > 0)
|
||||
{
|
||||
h.HavingCondition += ")";
|
||||
h.HavingOpenBracketsCount--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,6 +731,12 @@ namespace DynamORM.Builders.Implementation
|
||||
return "COUNT(*)";
|
||||
|
||||
return string.Format("COUNT({0})", Parse(node.Arguments[0], ref columnSchema, pars: Parameters, nulls: true));
|
||||
|
||||
case "COUNT0":
|
||||
if (node.Arguments != null && node.Arguments.Length > 0)
|
||||
throw new ArgumentException("COUNT0 method doesn't expect arguments");
|
||||
|
||||
return "COUNT(0)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ using DynamORM.Mapper;
|
||||
namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
/// <summary>Implementation of dynamic select query builder.</summary>
|
||||
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving
|
||||
{
|
||||
private int? _limit = null;
|
||||
private int? _offset = null;
|
||||
@@ -54,6 +54,16 @@ namespace DynamORM.Builders.Implementation
|
||||
private string _groupby;
|
||||
private string _orderby;
|
||||
|
||||
#region IQueryWithHaving
|
||||
|
||||
/// <summary>Gets or sets the having condition.</summary>
|
||||
public string HavingCondition { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the amount of not closed brackets in having statement.</summary>
|
||||
public int HavingOpenBracketsCount { get; set; }
|
||||
|
||||
#endregion IQueryWithHaving
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has select columns.
|
||||
/// </summary>
|
||||
@@ -113,6 +123,7 @@ namespace DynamORM.Builders.Implementation
|
||||
if (_join != null) sb.AppendFormat(" {0}", _join);
|
||||
if (WhereCondition != null) sb.AppendFormat(" WHERE {0}", WhereCondition);
|
||||
if (_groupby != null) sb.AppendFormat(" GROUP BY {0}", _groupby);
|
||||
if (HavingCondition != null) sb.AppendFormat(" HAVING {0}", HavingCondition);
|
||||
if (_orderby != null) sb.AppendFormat(" ORDER BY {0}", _orderby);
|
||||
if (_limit.HasValue && !lused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
|
||||
sb.AppendFormat(" LIMIT {0}", _limit);
|
||||
@@ -1012,6 +1023,62 @@ namespace DynamORM.Builders.Implementation
|
||||
|
||||
#endregion GroupBy
|
||||
|
||||
#region Having
|
||||
|
||||
/// <summary>
|
||||
/// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition
|
||||
/// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used
|
||||
/// as needed.
|
||||
/// <para>- If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator.</para>
|
||||
/// <para>- The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in:
|
||||
/// 'Having( x => x.Or( condition ) )'.</para>
|
||||
/// </summary>
|
||||
/// <param name="func">The specification.</param>
|
||||
/// <returns>This instance to permit chaining.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(Func<dynamic, object> func)
|
||||
{
|
||||
return this.InternalHaving(func);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column with operator and value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(DynamicColumn column)
|
||||
{
|
||||
return this.InternalHaving(column);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="op">Condition operator.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value)
|
||||
{
|
||||
return this.InternalHaving(column, op, value);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="column">Condition column.</param>
|
||||
/// <param name="value">Condition value.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(string column, object value)
|
||||
{
|
||||
return this.InternalHaving(column, value);
|
||||
}
|
||||
|
||||
/// <summary>Add Having condition.</summary>
|
||||
/// <param name="conditions">Set conditions as properties and values of an object.</param>
|
||||
/// <param name="schema">If <c>true</c> use schema to determine key columns and ignore those which
|
||||
/// aren't keys.</param>
|
||||
/// <returns>Builder instance.</returns>
|
||||
public virtual IDynamicSelectQueryBuilder Having(object conditions, bool schema = false)
|
||||
{
|
||||
return this.InternalHaving(conditions, schema);
|
||||
}
|
||||
|
||||
#endregion Having
|
||||
|
||||
#region OrderBy
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Builders\Extensions\DynamicHavingQueryExtensions.cs" />
|
||||
<Compile Include="Builders\IDynamicInsertQueryBuilder.cs" />
|
||||
<Compile Include="Builders\IDynamicSelectQueryBuilder.cs" />
|
||||
<Compile Include="Builders\IDynamicUpdateQueryBuilder.cs" />
|
||||
|
||||
496
DynamORM/DynamicCachedReader.cs
Normal file
496
DynamORM/DynamicCachedReader.cs
Normal file
@@ -0,0 +1,496 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using DynamORM.Helpers;
|
||||
using DynamORM.Mapper;
|
||||
|
||||
namespace DynamORM
|
||||
{
|
||||
/// <summary>Cache data reader in memory.</summary>
|
||||
internal class DynamicCachedReader : DynamicObject, IDataReader
|
||||
{
|
||||
#region Constructor and Data
|
||||
|
||||
private DataTable _schema;
|
||||
private int _fields;
|
||||
private int _rows;
|
||||
private int _position;
|
||||
private int _cachePos;
|
||||
|
||||
private IList<string> _names;
|
||||
private IDictionary<string, int> _ordinals;
|
||||
private IList<Type> _types;
|
||||
private IList<object> _cache;
|
||||
|
||||
private DynamicCachedReader()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DynamicCachedReader"/> class.</summary>
|
||||
/// <param name="reader">Reader to cache.</param>
|
||||
public DynamicCachedReader(IDataReader reader)
|
||||
{
|
||||
InitDataReader(reader);
|
||||
}
|
||||
|
||||
#endregion Constructor and Data
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>Create data reader from enumerable.</summary>
|
||||
/// <typeparam name="T">Type of enumerated objects.</typeparam>
|
||||
/// <param name="objects">List of objects.</param>
|
||||
/// <returns>Instance of <see cref="DynamicCachedReader"/> containing objects data.</returns>
|
||||
public static DynamicCachedReader FromEnumerable<T>(IEnumerable<T> objects)
|
||||
{
|
||||
var mapper = DynamicMapperCache.GetMapper<T>();
|
||||
|
||||
if (mapper == null)
|
||||
throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", typeof(T).FullName));
|
||||
|
||||
var r = new DynamicCachedReader();
|
||||
r.Init(mapper.ColumnsMap.Count + 1);
|
||||
r.CreateSchemaTable(mapper);
|
||||
r.FillFromEnumerable(objects, mapper);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private void InitDataReader(IDataReader reader)
|
||||
{
|
||||
_schema = reader.GetSchemaTable();
|
||||
|
||||
Init(reader.FieldCount);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < _fields; i++)
|
||||
{
|
||||
_names.Add(reader.GetName(i));
|
||||
_types.Add(reader.GetFieldType(i));
|
||||
|
||||
if (!_ordinals.ContainsKey(reader.GetName(i).ToUpper()))
|
||||
_ordinals.Add(reader.GetName(i).ToUpper(), i);
|
||||
}
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
for (i = 0; i < _fields; i++)
|
||||
_cache.Add(reader[i]);
|
||||
|
||||
_rows++;
|
||||
}
|
||||
|
||||
IsClosed = false;
|
||||
_position = -1;
|
||||
_cachePos = -1;
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
private void FillFromEnumerable<T>(IEnumerable<T> objects, DynamicTypeMap mapper)
|
||||
{
|
||||
foreach (var elem in objects)
|
||||
{
|
||||
foreach (var col in mapper.ColumnsMap)
|
||||
{
|
||||
object val = null;
|
||||
|
||||
if (col.Value.Get != null)
|
||||
val = col.Value.Get(elem);
|
||||
|
||||
_cache.Add(val);
|
||||
}
|
||||
|
||||
_cache.Add(elem);
|
||||
|
||||
_rows++;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSchemaTable(DynamicTypeMap mapper)
|
||||
{
|
||||
_schema = new DataTable("DYNAMIC");
|
||||
_schema.Columns.Add(new DataColumn("ColumnName", typeof(string)));
|
||||
_schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("ColumnSize", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short)));
|
||||
_schema.Columns.Add(new DataColumn("NumericScale", typeof(short)));
|
||||
_schema.Columns.Add(new DataColumn("DataType", typeof(Type)));
|
||||
_schema.Columns.Add(new DataColumn("ProviderType", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("NativeType", typeof(int)));
|
||||
_schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsUnique", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsKey", typeof(bool)));
|
||||
_schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool)));
|
||||
|
||||
int ordinal = 0;
|
||||
DataRow dr = null;
|
||||
|
||||
foreach (var column in mapper.ColumnsMap)
|
||||
{
|
||||
dr = _schema.NewRow();
|
||||
|
||||
dr[0] = column.Value.Column.NullOr(x => x.Name ?? column.Value.Name, column.Value.Name);
|
||||
dr[1] = ordinal;
|
||||
dr[2] = column.Value.Column.NullOr(x => x.Size ?? int.MaxValue, int.MaxValue);
|
||||
dr[3] = column.Value.Column.NullOr(x => x.Precision ?? 0, 0);
|
||||
dr[4] = column.Value.Column.NullOr(x => x.Scale ?? 0, 0);
|
||||
dr[5] = column.Value.Column.NullOr(x => x.Type.HasValue ? x.Type.Value.ToType() : column.Value.Type, column.Value.Type);
|
||||
dr[6] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
|
||||
dr[7] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
|
||||
dr[8] = !column.Value.Column.NullOr(x => x.IsKey, false);
|
||||
dr[9] = false;
|
||||
dr[10] = column.Value.Column.NullOr(x => x.IsKey, false);
|
||||
dr[11] = false;
|
||||
|
||||
_schema.Rows.Add(dr);
|
||||
|
||||
_names.Add(dr[0].ToString());
|
||||
_ordinals.Add(dr[0].ToString().ToUpper(), ordinal++);
|
||||
_types.Add((Type)dr[5]);
|
||||
|
||||
dr.AcceptChanges();
|
||||
}
|
||||
|
||||
dr = _schema.NewRow();
|
||||
|
||||
dr[0] = "#O";
|
||||
dr[1] = ordinal;
|
||||
dr[2] = int.MaxValue;
|
||||
dr[3] = 0;
|
||||
dr[4] = 0;
|
||||
dr[5] = mapper.Type;
|
||||
dr[6] = DbType.Object;
|
||||
dr[7] = DbType.Object;
|
||||
dr[8] = true;
|
||||
dr[9] = false;
|
||||
dr[10] = false;
|
||||
dr[11] = false;
|
||||
|
||||
_schema.Rows.Add(dr);
|
||||
|
||||
_names.Add("#O");
|
||||
_ordinals.Add("#O".ToUpper(), ordinal++);
|
||||
_types.Add(mapper.Type);
|
||||
|
||||
dr.AcceptChanges();
|
||||
}
|
||||
|
||||
private void Init(int fieldCount)
|
||||
{
|
||||
_rows = 0;
|
||||
_fields = fieldCount;
|
||||
_names = new List<string>(_fields);
|
||||
_ordinals = new Dictionary<string, int>(_fields);
|
||||
_types = new List<Type>(_fields);
|
||||
_cache = new List<object>(_fields * 100);
|
||||
}
|
||||
|
||||
#endregion Helpers
|
||||
|
||||
#region IDataReader Members
|
||||
|
||||
/// <summary>Closes the System.Data.IDataReader Object.</summary>
|
||||
public void Close()
|
||||
{
|
||||
IsClosed = true;
|
||||
_position = _rows;
|
||||
_cachePos = -1;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating the depth of nesting for the current row.</summary>
|
||||
/// <remarks>This implementation use this field to indicate row count.</remarks>
|
||||
public int Depth
|
||||
{
|
||||
get { return _rows; }
|
||||
}
|
||||
|
||||
/// <summary>Returns a System.Data.DataTable that describes the column metadata of the
|
||||
/// System.Data.IDataReader.</summary><returns>A System.Data.DataTable that describes
|
||||
/// the column metadata.</returns><exception cref="System.InvalidOperationException">
|
||||
/// The System.Data.IDataReader is closed.</exception>
|
||||
public DataTable GetSchemaTable()
|
||||
{
|
||||
return _schema;
|
||||
}
|
||||
|
||||
/// <summary>Gets a value indicating whether the data reader is closed.</summary>
|
||||
public bool IsClosed { get; private set; }
|
||||
|
||||
/// <summary>Advances the data reader to the next result, when reading the results of batch SQL statements.</summary>
|
||||
/// <returns>Returns true if there are more rows; otherwise, false.</returns>
|
||||
public bool NextResult()
|
||||
{
|
||||
_cachePos = (++_position) * _fields;
|
||||
|
||||
return _position < _rows;
|
||||
}
|
||||
|
||||
/// <summary>Advances the System.Data.IDataReader to the next record.</summary>
|
||||
/// <returns>Returns true if there are more rows; otherwise, false.</returns>
|
||||
public bool Read()
|
||||
{
|
||||
_cachePos = (++_position) * _fields;
|
||||
|
||||
return _position < _rows;
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.</summary>
|
||||
/// <returns>The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement
|
||||
/// failed; and -1 for SELECT statements.</returns>
|
||||
public int RecordsAffected { get { return 0; } }
|
||||
|
||||
#endregion IDataReader Members
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with
|
||||
/// freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_names.Clear();
|
||||
_types.Clear();
|
||||
_cache.Clear();
|
||||
_schema.Dispose();
|
||||
}
|
||||
|
||||
#endregion IDisposable Members
|
||||
|
||||
#region IDataRecord Members
|
||||
|
||||
/// <summary>Gets the number of columns in the current row.</summary>
|
||||
/// <remarks>When not positioned in a valid record set, 0; otherwise, the number of columns in the current record. The default is -1.</remarks>
|
||||
public int FieldCount { get { return _fields; } }
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public bool GetBoolean(int i)
|
||||
{
|
||||
return (bool)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public byte GetByte(int i)
|
||||
{
|
||||
return (byte)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Reads a stream of bytes from the specified column offset into the buffer
|
||||
/// as an array, starting at the given buffer offset.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <param name="fieldOffset">The index within the field from which to start the read operation.</param>
|
||||
/// <param name="buffer">The buffer into which to read the stream of bytes.</param>
|
||||
/// <param name="bufferoffset">The index for buffer to start the read operation.</param>
|
||||
/// <param name="length">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of bytes read.</returns>
|
||||
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
|
||||
return ms.Read(buffer, bufferoffset, length);
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public char GetChar(int i)
|
||||
{
|
||||
return (char)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Reads a stream of characters from the specified column offset into the buffer
|
||||
/// as an array, starting at the given buffer offset.</summary>
|
||||
/// <param name="i">The zero-based column ordinal.</param>
|
||||
/// <param name="fieldoffset">The index within the row from which to start the read operation.</param>
|
||||
/// <param name="buffer">The buffer into which to read the stream of bytes.</param>
|
||||
/// <param name="bufferoffset">The index for buffer to start the read operation.</param>
|
||||
/// <param name="length">The number of bytes to read.</param>
|
||||
/// <returns>The actual number of characters read.</returns>
|
||||
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
|
||||
{
|
||||
byte[] buff = new byte[buffer.Length];
|
||||
long ret = ms.Read(buff, bufferoffset, length);
|
||||
|
||||
for (int n = bufferoffset; n < ret; n++)
|
||||
buffer[n] = (char)buff[n];
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns an System.Data.IDataReader for the specified column ordinal.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>An System.Data.IDataReader.</returns>
|
||||
public IDataReader GetData(int i)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Gets the data type information for the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The data type information for the specified field.</returns>
|
||||
public string GetDataTypeName(int i)
|
||||
{
|
||||
return _types[i].Name;
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public DateTime GetDateTime(int i)
|
||||
{
|
||||
return (DateTime)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public decimal GetDecimal(int i)
|
||||
{
|
||||
return (decimal)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public double GetDouble(int i)
|
||||
{
|
||||
return (double)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets the System.Type information corresponding to the type of System.Object
|
||||
/// that would be returned from System.Data.IDataRecord.GetValue(System.Int32).</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The System.Type information corresponding to the type of System.Object that
|
||||
/// would be returned from System.Data.IDataRecord.GetValue(System.Int32).</returns>
|
||||
public Type GetFieldType(int i)
|
||||
{
|
||||
return _types[i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public float GetFloat(int i)
|
||||
{
|
||||
return (float)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public Guid GetGuid(int i)
|
||||
{
|
||||
return (Guid)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public short GetInt16(int i)
|
||||
{
|
||||
return (short)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public int GetInt32(int i)
|
||||
{
|
||||
return (int)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public long GetInt64(int i)
|
||||
{
|
||||
return (long)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets the name for the field to find.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>The name of the field or the empty string (""), if there is no value to return.</returns>
|
||||
public string GetName(int i)
|
||||
{
|
||||
return _names[i];
|
||||
}
|
||||
|
||||
/// <summary>Return the index of the named field.</summary>
|
||||
/// <param name="name">The name of the field to find.</param>
|
||||
/// <returns>The index of the named field.</returns>
|
||||
public int GetOrdinal(string name)
|
||||
{
|
||||
if (_ordinals.ContainsKey(name.ToUpper()))
|
||||
return _ordinals[name.ToUpper()];
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public string GetString(int i)
|
||||
{
|
||||
return (string)_cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Return the value of the specified field.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Field value upon return.</returns>
|
||||
public object GetValue(int i)
|
||||
{
|
||||
return _cache[_cachePos + i];
|
||||
}
|
||||
|
||||
/// <summary>Gets all the attribute fields in the collection for the current record.</summary>
|
||||
/// <param name="values">An array of System.Object to copy the attribute fields into.</param>
|
||||
/// <returns>The number of instances of System.Object in the array.</returns>
|
||||
public int GetValues(object[] values)
|
||||
{
|
||||
for (int i = 0; i < _fields; i++)
|
||||
values[i] = _cache[_cachePos + i];
|
||||
|
||||
return _fields;
|
||||
}
|
||||
|
||||
/// <summary>Return whether the specified field is set to null.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Returns true if the specified field is set to null; otherwise, false.</returns>
|
||||
public bool IsDBNull(int i)
|
||||
{
|
||||
return _cache[_cachePos + i] == null || _cache[_cachePos + i] == DBNull.Value;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets specified value in current record.</summary>
|
||||
/// <param name="name">Name of column.</param>
|
||||
/// <returns>Value of specified column.</returns>
|
||||
public object this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ordinals.ContainsKey(name.ToUpper()))
|
||||
return _cache[_cachePos + _ordinals[name.ToUpper()]];
|
||||
|
||||
throw new IndexOutOfRangeException(String.Format("Field '{0}' not found.", name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets specified value in current record.</summary>
|
||||
/// <param name="i">The index of the field to find.</param>
|
||||
/// <returns>Value of specified column.</returns>
|
||||
public object this[int i]
|
||||
{
|
||||
get { return _cache[_cachePos + i]; }
|
||||
}
|
||||
|
||||
#endregion IDataRecord Members
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user