Files
DynamORM/DynamORM/DynamicColumn.cs
2022-02-22 07:52:06 +00:00

510 lines
19 KiB
C#

/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2015, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*
* 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 System.Data;
using System.Text;
namespace DynamORM
{
/// <summary>Small utility class to manage single columns.</summary>
public class DynamicColumn
{
#region Enums
/// <summary>Order By Order.</summary>
public enum SortOrder
{
/// <summary>Ascending order.</summary>
Asc,
/// <summary>Descending order.</summary>
Desc
}
/// <summary>Dynamic query operators.</summary>
public enum CompareOperator
{
/// <summary>Equals operator (default).</summary>
Eq,
/// <summary>Not equal operator.</summary>
Not,
/// <summary>Like operator.</summary>
Like,
/// <summary>Not like operator.</summary>
NotLike,
/// <summary>In operator.</summary>
In,
/// <summary>Less than operator.</summary>
Lt,
/// <summary>Less or equal operator.</summary>
Lte,
/// <summary>Greater than operator.</summary>
Gt,
/// <summary>Greater or equal operator.</summary>
Gte,
/// <summary>Between two values.</summary>
Between,
}
#endregion Enums
#region Constructors
/// <summary>Initializes a new instance of the <see cref="DynamicColumn" /> class.</summary>
public DynamicColumn()
{
ParameterDirection = ParameterDirection.Input;
}
/// <summary>Initializes a new instance of the <see cref="DynamicColumn" /> class.</summary>
/// <remarks>Constructor provided for easier object creation in queries.</remarks>
/// <param name="columnName">Name of column to set.</param>
public DynamicColumn(string columnName)
: this()
{
ColumnName = columnName;
}
/// <summary>Initializes a new instance of the <see cref="DynamicColumn" /> class.</summary>
/// <remarks>Constructor provided for easier object creation in queries.</remarks>
/// <param name="columnName">Name of column to set.</param>
/// <param name="oper">Compare column to value(s) operator.</param>
/// <param name="value">Parameter value(s).</param>
public DynamicColumn(string columnName, CompareOperator oper, object value)
: this(columnName)
{
Operator = oper;
Value = value;
}
#endregion Constructors
#region Properties
/// <summary>Gets or sets column name.</summary>
public string ColumnName { get; set; }
/// <summary>Gets or sets column alias.</summary>
/// <remarks>Select specific.</remarks>
public string Alias { get; set; }
/// <summary>Gets or sets aggregate function used on column.</summary>
/// <remarks>Select specific.</remarks>
public string Aggregate { get; set; }
/// <summary>Gets or sets order direction.</summary>
public SortOrder Order { get; set; }
/// <summary>Gets or sets parameter direction when used in procedure invocation.</summary>
public ParameterDirection ParameterDirection { get; set; }
/// <summary>Gets or sets value for parameters.</summary>
public object Value { get; set; }
/// <summary>Gets or sets condition operator.</summary>
public CompareOperator Operator { get; set; }
/// <summary>Gets or sets a value indicating whether this condition will be treated as or condition.</summary>
public bool Or { get; set; }
/// <summary>Gets or sets a value indicating whether start new block in where statement.</summary>
public bool BeginBlock { get; set; }
/// <summary>Gets or sets a value indicating whether end existing block in where statement.</summary>
public bool EndBlock { get; set; }
/// <summary>Gets or sets a value indicating whether set parameters for null values.</summary>
public bool? VirtualColumn { get; set; }
/// <summary>Gets or sets schema representation of a column.</summary>
/// <remarks>Workaround to providers issues which sometimes pass wrong
/// data o schema. For example decimal has precision of 255 in SQL
/// server.</remarks>
public DynamicSchemaColumn? Schema { get; set; }
#endregion Properties
#region Query creation helpers
#region Operators
private DynamicColumn SetOperatorAndValue(CompareOperator c, object v)
{
Operator = c;
Value = v;
return this;
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Eq"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Eq(object value)
{
return SetOperatorAndValue(CompareOperator.Eq, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Not"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Not(object value)
{
return SetOperatorAndValue(CompareOperator.Not, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Like"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Like(object value)
{
return SetOperatorAndValue(CompareOperator.Like, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.NotLike"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn NotLike(object value)
{
return SetOperatorAndValue(CompareOperator.NotLike, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Gt"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Greater(object value)
{
return SetOperatorAndValue(CompareOperator.Gt, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Lt"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Less(object value)
{
return SetOperatorAndValue(CompareOperator.Lt, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Gte"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn GreaterOrEqual(object value)
{
return SetOperatorAndValue(CompareOperator.Gte, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Lte"/> and
/// <see cref="DynamicColumn.Value"/> to provided <c>value</c>.</summary>
/// <param name="value">Value of parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn LessOrEqual(object value)
{
return SetOperatorAndValue(CompareOperator.Lte, value);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.Between"/> and
/// <see cref="DynamicColumn.Value"/> to provided values.</summary>
/// <param name="from">Value of from parameter to set.</param>
/// <param name="to">Value of to parameter to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn Between(object from, object to)
{
return SetOperatorAndValue(CompareOperator.Between, new[] { from, to });
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.In"/> and
/// <see cref="DynamicColumn.Value"/> to provided values.</summary>
/// <param name="values">Values of parameters to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn In(IEnumerable<object> values)
{
return SetOperatorAndValue(CompareOperator.In, values);
}
/// <summary>Helper method setting <see cref="DynamicColumn.Operator"/>
/// to <see cref="CompareOperator.In"/> and
/// <see cref="DynamicColumn.Value"/> to provided values.</summary>
/// <param name="values">Values of parameters to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn In(params object[] values)
{
if (values.Length == 1 && (values[0].GetType().IsCollection() || values[0] is IEnumerable<object>))
return SetOperatorAndValue(CompareOperator.In, values[0]);
return SetOperatorAndValue(CompareOperator.In, values);
}
#endregion Operators
#region Order
/// <summary>Helper method setting <see cref="DynamicColumn.Order"/>
/// to <see cref="SortOrder.Asc"/>..</summary>
/// <returns>Returns self.</returns>
public DynamicColumn Asc()
{
Order = SortOrder.Asc;
return this;
}
/// <summary>Helper method setting <see cref="DynamicColumn.Order"/>
/// to <see cref="SortOrder.Desc"/>..</summary>
/// <returns>Returns self.</returns>
public DynamicColumn Desc()
{
Order = SortOrder.Desc;
return this;
}
#endregion Order
#region Other
/// <summary>Helper method setting
/// <see cref="DynamicColumn.ColumnName"/>
/// to provided <c>name</c>.</summary>
/// <param name="name">Name to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetName(string name)
{
ColumnName = name;
return this;
}
/// <summary>Helper method setting
/// <see cref="DynamicColumn.Alias"/>
/// to provided <c>alias</c>.</summary>
/// <param name="alias">Alias to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetAlias(string alias)
{
Alias = alias;
return this;
}
/// <summary>Helper method setting
/// <see cref="DynamicColumn.Aggregate"/>
/// to provided <c>aggregate</c>.</summary>
/// <param name="aggregate">Aggregate to set.</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetAggregate(string aggregate)
{
Aggregate = aggregate;
return this;
}
/// <summary>Sets the begin block flag.</summary>
/// <param name="begin">If set to <c>true</c> [begin].</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetBeginBlock(bool begin = true)
{
BeginBlock = begin;
return this;
}
/// <summary>Sets the end block flag.</summary>
/// <param name="end">If set to <c>true</c> [end].</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetEndBlock(bool end = true)
{
EndBlock = end;
return this;
}
/// <summary>Sets the or flag.</summary>
/// <param name="or">If set to <c>true</c> [or].</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetOr(bool or = true)
{
Or = or;
return this;
}
/// <summary>Sets the virtual column.</summary>
/// <param name="virt">Set virtual column value.</param>
/// <returns>Returns self.</returns>
public DynamicColumn SetVirtualColumn(bool? virt)
{
VirtualColumn = virt;
return this;
}
#endregion Other
#endregion Query creation helpers
#region Parsing
/// <summary>Parse column for select query.</summary>
/// <remarks>Column format consist of <c>Column Name</c>, <c>Alias</c> and
/// <c>Aggregate function</c> in this order separated by '<c>:</c>'.</remarks>
/// <param name="column">Column string.</param>
/// <returns>Instance of <see cref="DynamicColumn"/>.</returns>
public static DynamicColumn ParseSelectColumn(string column)
{
// Split column description
string[] parts = column.Split(':');
if (parts.Length > 0)
{
DynamicColumn ret = new DynamicColumn() { ColumnName = parts[0] };
if (parts.Length > 1)
ret.Alias = parts[1];
if (parts.Length > 2)
ret.Aggregate = parts[2];
return ret;
}
return null;
}
/// <summary>Parse column for order by in query.</summary>
/// <remarks>Column format consist of <c>Column Name</c> and
/// <c>Direction</c> in this order separated by '<c>:</c>'.</remarks>
/// <param name="column">Column string.</param>
/// <returns>Instance of <see cref="DynamicColumn"/>.</returns>
public static DynamicColumn ParseOrderByColumn(string column)
{
// Split column description
string[] parts = column.Split(':');
if (parts.Length > 0)
{
DynamicColumn ret = new DynamicColumn() { ColumnName = parts[0] };
if (parts.Length > 1)
ret.Order = parts[1].ToLower() == "d" || parts[1].ToLower() == "desc" ? SortOrder.Desc : SortOrder.Asc;
if (parts.Length > 2)
ret.Alias = parts[2];
return ret;
}
return null;
}
#endregion Parsing
#region ToSQL
internal string ToSQLSelectColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLSelectColumn(db, sb);
return sb.ToString();
}
internal void ToSQLSelectColumn(DynamicDatabase db, StringBuilder sb)
{
string column = ColumnName == "*" ? "*" : ColumnName;
if (column != "*" &&
(column.IndexOf(db.LeftDecorator) == -1 || column.IndexOf(db.RightDecorator) == -1) &&
(column.IndexOf('(') == -1 || column.IndexOf(')') == -1))
column = db.DecorateName(column);
string alias = Alias;
if (!string.IsNullOrEmpty(Aggregate))
{
sb.AppendFormat("{0}({1})", Aggregate, column);
alias = string.IsNullOrEmpty(alias) ?
ColumnName == "*" ? Guid.NewGuid().ToString() : ColumnName :
alias;
}
else
sb.Append(column);
if (!string.IsNullOrEmpty(alias))
sb.AppendFormat(" AS {0}", alias);
}
internal string ToSQLGroupByColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLGroupByColumn(db, sb);
return sb.ToString();
}
internal void ToSQLGroupByColumn(DynamicDatabase db, StringBuilder sb)
{
sb.Append(db.DecorateName(ColumnName));
}
internal string ToSQLOrderByColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLOrderByColumn(db, sb);
return sb.ToString();
}
internal void ToSQLOrderByColumn(DynamicDatabase db, StringBuilder sb)
{
if (!string.IsNullOrEmpty(Alias))
sb.Append(Alias);
else
sb.Append(db.DecorateName(ColumnName));
sb.AppendFormat(" {0}", Order.ToString().ToUpper());
}
#endregion ToSQL
}
}