Expand typed SQL DSL with predicates, subqueries and projections
This commit is contained in:
@@ -7226,6 +7226,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Add typed SQL DSL insert assignment.</summary>
|
||||
IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
|
||||
|
||||
/// <summary>Add typed SQL DSL insert assignments from object projection.</summary>
|
||||
IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values);
|
||||
}
|
||||
/// <summary>Typed select query builder for mapped entities.</summary>
|
||||
/// <typeparam name="T">Mapped entity type.</typeparam>
|
||||
@@ -7327,6 +7330,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Add typed SQL DSL assignment.</summary>
|
||||
IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
|
||||
|
||||
/// <summary>Add typed SQL DSL assignments from object projection.</summary>
|
||||
IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values);
|
||||
}
|
||||
/// <summary>Dynamic update query builder interface.</summary>
|
||||
/// <remarks>This interface it publicly available. Implementation should be hidden.</remarks>
|
||||
@@ -10724,7 +10730,7 @@ namespace DynamORM
|
||||
}
|
||||
public IDynamicTypedDeleteQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
|
||||
{
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
if (string.IsNullOrEmpty(WhereCondition))
|
||||
WhereCondition = condition;
|
||||
else
|
||||
@@ -10765,6 +10771,14 @@ namespace DynamORM
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
/// <summary>Typed wrapper over <see cref="DynamicInsertQueryBuilder"/> with property-to-column translation.</summary>
|
||||
/// <typeparam name="T">Mapped entity type.</typeparam>
|
||||
@@ -10795,12 +10809,30 @@ namespace DynamORM
|
||||
public IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
|
||||
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
return this;
|
||||
}
|
||||
public IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
object data = values(new TypedTableContext<T>(null));
|
||||
foreach (KeyValuePair<string, object> item in data.ToDictionary())
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
|
||||
string value = (item.Value as TypedSqlExpression) != null
|
||||
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
|
||||
: RenderValue(item.Value);
|
||||
|
||||
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public new IDynamicTypedInsertQueryBuilder<T> Values(Func<dynamic, object> fn, params Func<dynamic, object>[] func)
|
||||
{
|
||||
base.Values(fn, func);
|
||||
@@ -10824,6 +10856,14 @@ namespace DynamORM
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
/// <summary>Typed wrapper over <see cref="DynamicSelectQueryBuilder"/> with property-to-column translation.</summary>
|
||||
/// <typeparam name="T">Mapped entity type.</typeparam>
|
||||
@@ -10863,6 +10903,17 @@ namespace DynamORM
|
||||
{
|
||||
return _builder.Database.DecorateName(name);
|
||||
}
|
||||
public string RenderSubQuery(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException("query");
|
||||
|
||||
foreach (var item in query.Parameters)
|
||||
if (!_builder.Parameters.ContainsKey(item.Key))
|
||||
_builder.Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
private readonly DynamicTypeMap _mapper;
|
||||
|
||||
@@ -11633,7 +11684,7 @@ namespace DynamORM
|
||||
}
|
||||
public IDynamicTypedUpdateQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
|
||||
{
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
if (string.IsNullOrEmpty(WhereCondition))
|
||||
WhereCondition = condition;
|
||||
else
|
||||
@@ -11644,11 +11695,29 @@ namespace DynamORM
|
||||
public IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
string assignment = string.Format("{0} = {1}", column, value);
|
||||
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
|
||||
return this;
|
||||
}
|
||||
public IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
object data = values(new TypedTableContext<T>(null));
|
||||
foreach (KeyValuePair<string, object> item in data.ToDictionary())
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
|
||||
string value = (item.Value as TypedSqlExpression) != null
|
||||
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
|
||||
: RenderValue(item.Value);
|
||||
|
||||
string assignment = string.Format("{0} = {1}", column, value);
|
||||
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public new IDynamicTypedUpdateQueryBuilder<T> Update(string column, object value)
|
||||
{
|
||||
base.Update(column, value);
|
||||
@@ -11707,6 +11776,14 @@ namespace DynamORM
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
/// <summary>Update query builder.</summary>
|
||||
internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere
|
||||
@@ -11996,12 +12073,14 @@ namespace DynamORM
|
||||
private readonly Func<Type, string, string, Func<string, string>, string> _resolveColumn;
|
||||
private readonly Func<object, string> _renderValue;
|
||||
private readonly Func<string, string> _decorateName;
|
||||
private readonly Func<IDynamicSelectQueryBuilder, string> _renderSubQuery;
|
||||
|
||||
public ModifyRenderContext(Func<Type, string, string, Func<string, string>, string> resolveColumn, Func<object, string> renderValue, Func<string, string> decorateName)
|
||||
public ModifyRenderContext(Func<Type, string, string, Func<string, string>, string> resolveColumn, Func<object, string> renderValue, Func<string, string> decorateName, Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
_resolveColumn = resolveColumn;
|
||||
_renderValue = renderValue;
|
||||
_decorateName = decorateName;
|
||||
_renderSubQuery = renderSubQuery;
|
||||
}
|
||||
public string ResolveColumn(Type modelType, string memberName, string alias)
|
||||
{
|
||||
@@ -12015,6 +12094,10 @@ namespace DynamORM
|
||||
{
|
||||
return _decorateName(name);
|
||||
}
|
||||
public string RenderSubQuery(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return _renderSubQuery(query);
|
||||
}
|
||||
}
|
||||
public static string GetMappedColumn<T, TValue>(Expression<Func<T, TValue>> selector)
|
||||
{
|
||||
@@ -12036,24 +12119,26 @@ namespace DynamORM
|
||||
Func<TypedTableContext<T>, TypedSqlPredicate> predicate,
|
||||
string alias,
|
||||
Func<object, string> renderValue,
|
||||
Func<string, string> decorateName)
|
||||
Func<string, string> decorateName,
|
||||
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName);
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
|
||||
return predicate(new TypedTableContext<T>(alias)).Render(context);
|
||||
}
|
||||
public static string RenderExpression<T>(
|
||||
Func<TypedTableContext<T>, TypedSqlExpression> expression,
|
||||
string alias,
|
||||
Func<object, string> renderValue,
|
||||
Func<string, string> decorateName)
|
||||
Func<string, string> decorateName,
|
||||
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
if (expression == null)
|
||||
throw new ArgumentNullException("expression");
|
||||
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName);
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
|
||||
return expression(new TypedTableContext<T>(alias)).Render(context);
|
||||
}
|
||||
private static void ApplyWhereInternal(Type modelType, Action<string, DynamicColumn.CompareOperator, object> addCondition, Expression expression)
|
||||
@@ -12120,7 +12205,7 @@ namespace DynamORM
|
||||
? decorateName(mapped)
|
||||
: string.Format("{0}.{1}", alias, decorateName(mapped));
|
||||
}
|
||||
private static string GetMappedColumnByName(Type modelType, string memberName)
|
||||
internal static string GetMappedColumnByName(Type modelType, string memberName)
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType);
|
||||
if (mapper == null)
|
||||
@@ -15728,6 +15813,9 @@ namespace DynamORM
|
||||
|
||||
/// <summary>Decorate SQL identifier.</summary>
|
||||
string DecorateName(string name);
|
||||
|
||||
/// <summary>Render subquery SQL and merge any parameters into current context.</summary>
|
||||
string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query);
|
||||
}
|
||||
/// <summary>Entry point for the typed SQL DSL.</summary>
|
||||
public static class Sql
|
||||
@@ -15767,6 +15855,71 @@ namespace DynamORM
|
||||
{
|
||||
return Func<T>("COALESCE", expressions);
|
||||
}
|
||||
/// <summary>Create SUM expression.</summary>
|
||||
public static TypedSqlExpression<T> Sum<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("SUM", expression);
|
||||
}
|
||||
/// <summary>Create AVG expression.</summary>
|
||||
public static TypedSqlExpression<T> Avg<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("AVG", expression);
|
||||
}
|
||||
/// <summary>Create MIN expression.</summary>
|
||||
public static TypedSqlExpression<T> Min<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("MIN", expression);
|
||||
}
|
||||
/// <summary>Create MAX expression.</summary>
|
||||
public static TypedSqlExpression<T> Max<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("MAX", expression);
|
||||
}
|
||||
/// <summary>Create ABS expression.</summary>
|
||||
public static TypedSqlExpression<T> Abs<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("ABS", expression);
|
||||
}
|
||||
/// <summary>Create UPPER expression.</summary>
|
||||
public static TypedSqlExpression<string> Upper(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("UPPER", expression);
|
||||
}
|
||||
/// <summary>Create LOWER expression.</summary>
|
||||
public static TypedSqlExpression<string> Lower(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("LOWER", expression);
|
||||
}
|
||||
/// <summary>Create TRIM expression.</summary>
|
||||
public static TypedSqlExpression<string> Trim(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("TRIM", expression);
|
||||
}
|
||||
/// <summary>Create LENGTH expression.</summary>
|
||||
public static TypedSqlExpression<int> Length(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<int>("LENGTH", expression);
|
||||
}
|
||||
/// <summary>Create NULLIF expression.</summary>
|
||||
public static TypedSqlExpression<T> NullIf<T>(TypedSqlExpression left, TypedSqlExpression right)
|
||||
{
|
||||
return Func<T>("NULLIF", left, right);
|
||||
}
|
||||
/// <summary>Create CURRENT_TIMESTAMP expression.</summary>
|
||||
public static TypedSqlExpression<DateTime> CurrentTimestamp()
|
||||
{
|
||||
return Raw<DateTime>("CURRENT_TIMESTAMP");
|
||||
}
|
||||
/// <summary>Create scalar subquery expression.</summary>
|
||||
public static TypedSqlExpression<T> SubQuery<T>(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return new TypedSqlSubQueryExpression<T>(query);
|
||||
}
|
||||
/// <summary>Create EXISTS predicate.</summary>
|
||||
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return new TypedSqlExistsPredicate(query);
|
||||
}
|
||||
/// <summary>Create CASE expression builder.</summary>
|
||||
public static TypedSqlCaseBuilder<T> Case<T>()
|
||||
{
|
||||
@@ -15861,6 +16014,26 @@ namespace DynamORM
|
||||
{
|
||||
return new TypedSqlUnaryPredicate(this, "IS NOT NULL");
|
||||
}
|
||||
/// <summary>LIKE predicate.</summary>
|
||||
public TypedSqlPredicate Like(string pattern)
|
||||
{
|
||||
return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern));
|
||||
}
|
||||
/// <summary>IN predicate.</summary>
|
||||
public TypedSqlPredicate In(params object[] values)
|
||||
{
|
||||
return new TypedSqlInPredicate(this, values);
|
||||
}
|
||||
/// <summary>IN predicate.</summary>
|
||||
public TypedSqlPredicate In(IEnumerable values)
|
||||
{
|
||||
return new TypedSqlInPredicate(this, values);
|
||||
}
|
||||
/// <summary>BETWEEN predicate.</summary>
|
||||
public TypedSqlPredicate Between(object lower, object upper)
|
||||
{
|
||||
return new TypedSqlBetweenPredicate(this, lower is TypedSqlExpression ? (TypedSqlExpression)lower : Sql.Val(lower), upper is TypedSqlExpression ? (TypedSqlExpression)upper : Sql.Val(upper));
|
||||
}
|
||||
}
|
||||
/// <summary>Typed SQL expression.</summary>
|
||||
public abstract class TypedSqlExpression<T> : TypedSqlExpression
|
||||
@@ -15934,7 +16107,7 @@ namespace DynamORM
|
||||
return context.ResolveColumn(_modelType, _memberName, _alias);
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlValueExpression<T> : TypedSqlExpression<T>
|
||||
internal sealed class TypedSqlValueExpression<T> : TypedSqlExpression<T>, ITypedSqlNullValue
|
||||
{
|
||||
private readonly object _value;
|
||||
|
||||
@@ -15946,6 +16119,10 @@ namespace DynamORM
|
||||
{
|
||||
return context.RenderValue(_value);
|
||||
}
|
||||
public bool IsNullValue
|
||||
{
|
||||
get { return _value == null; }
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlRawExpression<T> : TypedSqlExpression<T>
|
||||
{
|
||||
@@ -16009,12 +16186,56 @@ namespace DynamORM
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
string op = _operator;
|
||||
if (_right is TypedSqlValueExpression<object> && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase))
|
||||
TypedSqlValueExpression<object> objRight = _right as TypedSqlValueExpression<object>;
|
||||
if ((objRight != null && objRight.IsNullValue) || (_right is ITypedSqlNullValue && ((ITypedSqlNullValue)_right).IsNullValue))
|
||||
op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator;
|
||||
|
||||
return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context));
|
||||
}
|
||||
}
|
||||
internal interface ITypedSqlNullValue
|
||||
{
|
||||
bool IsNullValue { get; }
|
||||
}
|
||||
internal sealed class TypedSqlInPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlExpression _left;
|
||||
private readonly IEnumerable _values;
|
||||
|
||||
internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values)
|
||||
{
|
||||
_left = left;
|
||||
_values = values;
|
||||
}
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
List<string> rendered = new List<string>();
|
||||
foreach (object value in _values)
|
||||
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
||||
|
||||
if (rendered.Count == 0)
|
||||
return "(1 = 0)";
|
||||
|
||||
return string.Format("({0} IN({1}))", _left.Render(context), string.Join(", ", rendered.ToArray()));
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlExpression _left;
|
||||
private readonly TypedSqlExpression _lower;
|
||||
private readonly TypedSqlExpression _upper;
|
||||
|
||||
internal TypedSqlBetweenPredicate(TypedSqlExpression left, TypedSqlExpression lower, TypedSqlExpression upper)
|
||||
{
|
||||
_left = left;
|
||||
_lower = lower;
|
||||
_upper = upper;
|
||||
}
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("({0} BETWEEN {1} AND {2})", _left.Render(context), _lower.Render(context), _upper.Render(context));
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlPredicate _left;
|
||||
@@ -16070,6 +16291,32 @@ namespace DynamORM
|
||||
return string.Join(" ", items.ToArray());
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlSubQueryExpression<T> : TypedSqlExpression<T>
|
||||
{
|
||||
private readonly IDynamicSelectQueryBuilder _query;
|
||||
|
||||
internal TypedSqlSubQueryExpression(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("({0})", context.RenderSubQuery(_query));
|
||||
}
|
||||
}
|
||||
internal sealed class TypedSqlExistsPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly IDynamicSelectQueryBuilder _query;
|
||||
|
||||
internal TypedSqlExistsPredicate(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("(EXISTS ({0}))", context.RenderSubQuery(_query));
|
||||
}
|
||||
}
|
||||
/// <summary>Typed table context used by the typed SQL DSL.</summary>
|
||||
/// <typeparam name="T">Mapped entity type.</typeparam>
|
||||
public sealed class TypedTableContext<T>
|
||||
|
||||
@@ -115,5 +115,134 @@ namespace DynamORM.Tests.TypedSql
|
||||
"DELETE FROM \"sample_users\" WHERE ((\"id\" = [$0]) AND (\"code\" <> [$1]))",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWhereSqlSupportsLikeInAndBetween()
|
||||
{
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.WhereSql(u => u.Col(x => x.Code).Like("A%")
|
||||
.And(u.Col(x => x.Id).In(1, 2, 3))
|
||||
.And(u.Col(x => x.Id).Between(1, 10)))
|
||||
.SelectSql(u => u.Col(x => x.Id));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (((u.\"user_code\" LIKE [$0]) AND (u.\"id_user\" IN([$1], [$2], [$3]))) AND (u.\"id_user\" BETWEEN [$4] AND [$5]))",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWhereSqlSupportsEmptyIn()
|
||||
{
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.WhereSql(u => u.Col(x => x.Id).In(new int[0]))
|
||||
.SelectSql(u => u.Col(x => x.Id));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 0)",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectSqlSupportsStandardFunctions()
|
||||
{
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.SelectSql(
|
||||
u => Sql.Sum<int>(u.Col(x => x.Id)).As("sum_id"),
|
||||
u => Sql.Avg<int>(u.Col(x => x.Id)).As("avg_id"),
|
||||
u => Sql.Min<int>(u.Col(x => x.Id)).As("min_id"),
|
||||
u => Sql.Max<int>(u.Col(x => x.Id)).As("max_id"),
|
||||
u => Sql.Abs<int>(Sql.Val(-5)).As("abs_value"),
|
||||
u => Sql.Upper(u.Col(x => x.Code)).As("upper_code"),
|
||||
u => Sql.Lower(u.Col(x => x.Code)).As("lower_code"),
|
||||
u => Sql.Trim(u.Col(x => x.Code)).As("trim_code"),
|
||||
u => Sql.Length(u.Col(x => x.Code)).As("len_code"),
|
||||
u => Sql.NullIf<string>(u.Col(x => x.Code), Sql.Val("X")).As("nullif_code"),
|
||||
u => Sql.CurrentTimestamp().As("ts"));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT SUM(u.\"id_user\") AS \"sum_id\", AVG(u.\"id_user\") AS \"avg_id\", MIN(u.\"id_user\") AS \"min_id\", MAX(u.\"id_user\") AS \"max_id\", ABS([$0]) AS \"abs_value\", UPPER(u.\"user_code\") AS \"upper_code\", LOWER(u.\"user_code\") AS \"lower_code\", TRIM(u.\"user_code\") AS \"trim_code\", LENGTH(u.\"user_code\") AS \"len_code\", NULLIF(u.\"user_code\", [$1]) AS \"nullif_code\", CURRENT_TIMESTAMP AS \"ts\" FROM \"sample_users\" AS u",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectSqlSupportsCustomFunction()
|
||||
{
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.SelectSql(u => Sql.Func<string>("CUSTOM_FUNC", u.Col(x => x.Code), Sql.Val(5)).As("custom_value"));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT CUSTOM_FUNC(u.\"user_code\", [$0]) AS \"custom_value\" FROM \"sample_users\" AS u",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectSqlSupportsScalarSubQuery()
|
||||
{
|
||||
var sq = Database.From(x => x.sample_users.As("x"))
|
||||
.Select(x => x.x.id_user)
|
||||
.Where(x => x.x.user_code == "A");
|
||||
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.SelectSql(u => Sql.SubQuery<long>(sq).As("sub_id"));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])) AS \"sub_id\" FROM \"sample_users\" AS u",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWhereSqlSupportsExists()
|
||||
{
|
||||
var sq = Database.From(x => x.sample_users.As("x"))
|
||||
.Select(x => x.x.id_user)
|
||||
.Where(x => x.x.user_code == "A");
|
||||
|
||||
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||
.WhereSql(u => Sql.Exists(sq))
|
||||
.SelectSql(u => u.Col(x => x.Id));
|
||||
|
||||
Assert.AreEqual(
|
||||
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT x.\"id_user\" FROM \"sample_users\" AS x WHERE (x.\"user_code\" = [$0])))",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInsertSqlObjectProjection()
|
||||
{
|
||||
var sq = Database.From(x => x.sample_users.As("x"))
|
||||
.Select(x => x.x.user_code)
|
||||
.Where(x => x.x.id_user == 1);
|
||||
|
||||
var cmd = Database.InsertTyped<Users>()
|
||||
.InsertSql(u => new
|
||||
{
|
||||
Code = Sql.SubQuery<string>(sq),
|
||||
First = Sql.Upper(Sql.Val("typed"))
|
||||
});
|
||||
|
||||
Assert.AreEqual(
|
||||
"INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ((SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), UPPER([$1]))",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUpdateSqlObjectProjection()
|
||||
{
|
||||
var sq = Database.From(x => x.sample_users.As("x"))
|
||||
.Select(x => x.x.user_code)
|
||||
.Where(x => x.x.id_user == 1);
|
||||
|
||||
var cmd = Database.UpdateTyped<Users>()
|
||||
.SetSql(u => new
|
||||
{
|
||||
Code = Sql.SubQuery<string>(sq),
|
||||
First = Sql.Lower(Sql.Val("TYPED"))
|
||||
})
|
||||
.WhereSql(u => u.Col(x => x.Id).Eq(1));
|
||||
|
||||
Assert.AreEqual(
|
||||
"UPDATE \"sample_users\" SET \"code\" = (SELECT x.\"user_code\" FROM \"sample_users\" AS x WHERE (x.\"id_user\" = [$0])), \"first\" = LOWER([$1]) WHERE (\"id\" = [$2])",
|
||||
NormalizeSql(cmd.CommandText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +28,8 @@ namespace DynamORM.Builders
|
||||
|
||||
/// <summary>Add typed SQL DSL insert assignment.</summary>
|
||||
IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
|
||||
|
||||
/// <summary>Add typed SQL DSL insert assignments from object projection.</summary>
|
||||
IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,5 +36,8 @@ namespace DynamORM.Builders
|
||||
|
||||
/// <summary>Add typed SQL DSL assignment.</summary>
|
||||
IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory);
|
||||
|
||||
/// <summary>Add typed SQL DSL assignments from object projection.</summary>
|
||||
IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.TypedSql;
|
||||
@@ -37,7 +38,7 @@ namespace DynamORM.Builders.Implementation
|
||||
|
||||
public IDynamicTypedDeleteQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
|
||||
{
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
if (string.IsNullOrEmpty(WhereCondition))
|
||||
WhereCondition = condition;
|
||||
else
|
||||
@@ -84,5 +85,14 @@ namespace DynamORM.Builders.Implementation
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.TypedSql;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
@@ -44,13 +46,33 @@ namespace DynamORM.Builders.Implementation
|
||||
public IDynamicTypedInsertQueryBuilder<T> InsertSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
|
||||
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IDynamicTypedInsertQueryBuilder<T> InsertSql(Func<TypedTableContext<T>, object> values)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
object data = values(new TypedTableContext<T>(null));
|
||||
foreach (KeyValuePair<string, object> item in data.ToDictionary())
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
|
||||
string value = (item.Value as TypedSqlExpression) != null
|
||||
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
|
||||
: RenderValue(item.Value);
|
||||
|
||||
_columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column);
|
||||
_values = _values == null ? value : string.Format("{0}, {1}", _values, value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public new IDynamicTypedInsertQueryBuilder<T> Values(Func<dynamic, object> fn, params Func<dynamic, object>[] func)
|
||||
{
|
||||
base.Values(fn, func);
|
||||
@@ -77,5 +99,14 @@ namespace DynamORM.Builders.Implementation
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,18 @@ namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
return _builder.Database.DecorateName(name);
|
||||
}
|
||||
|
||||
public string RenderSubQuery(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException("query");
|
||||
|
||||
foreach (var item in query.Parameters)
|
||||
if (!_builder.Parameters.ContainsKey(item.Key))
|
||||
_builder.Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly DynamicTypeMap _mapper;
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using DynamORM.Builders.Extensions;
|
||||
using DynamORM.Helpers.Dynamics;
|
||||
using DynamORM.TypedSql;
|
||||
|
||||
namespace DynamORM.Builders.Implementation
|
||||
@@ -49,7 +51,7 @@ namespace DynamORM.Builders.Implementation
|
||||
|
||||
public IDynamicTypedUpdateQueryBuilder<T> WhereSql(Func<TypedTableContext<T>, TypedSqlPredicate> predicate)
|
||||
{
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName);
|
||||
string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
if (string.IsNullOrEmpty(WhereCondition))
|
||||
WhereCondition = condition;
|
||||
else
|
||||
@@ -61,12 +63,32 @@ namespace DynamORM.Builders.Implementation
|
||||
public IDynamicTypedUpdateQueryBuilder<T> SetSql<TValue>(Expression<Func<T, TValue>> selector, Func<TypedTableContext<T>, TypedSqlExpression> valueFactory)
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName);
|
||||
string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery);
|
||||
string assignment = string.Format("{0} = {1}", column, value);
|
||||
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IDynamicTypedUpdateQueryBuilder<T> SetSql(Func<TypedTableContext<T>, object> values)
|
||||
{
|
||||
if (values == null)
|
||||
throw new ArgumentNullException("values");
|
||||
|
||||
object data = values(new TypedTableContext<T>(null));
|
||||
foreach (KeyValuePair<string, object> item in data.ToDictionary())
|
||||
{
|
||||
string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true);
|
||||
string value = (item.Value as TypedSqlExpression) != null
|
||||
? TypedModifyHelper.RenderExpression<T>(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery)
|
||||
: RenderValue(item.Value);
|
||||
|
||||
string assignment = string.Format("{0} = {1}", column, value);
|
||||
_columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public new IDynamicTypedUpdateQueryBuilder<T> Update(string column, object value)
|
||||
{
|
||||
base.Update(column, value);
|
||||
@@ -135,5 +157,14 @@ namespace DynamORM.Builders.Implementation
|
||||
DynamicSchemaColumn? columnSchema = null;
|
||||
return ParseConstant(value, Parameters, columnSchema);
|
||||
}
|
||||
|
||||
private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
foreach (KeyValuePair<string, IParameter> item in query.Parameters)
|
||||
if (!Parameters.ContainsKey(item.Key))
|
||||
Parameters.Add(item.Key, item.Value);
|
||||
|
||||
return query.CommandText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using DynamORM.Builders;
|
||||
using DynamORM.Mapper;
|
||||
using DynamORM.TypedSql;
|
||||
|
||||
@@ -21,12 +22,14 @@ namespace DynamORM.Builders.Implementation
|
||||
private readonly Func<Type, string, string, Func<string, string>, string> _resolveColumn;
|
||||
private readonly Func<object, string> _renderValue;
|
||||
private readonly Func<string, string> _decorateName;
|
||||
private readonly Func<IDynamicSelectQueryBuilder, string> _renderSubQuery;
|
||||
|
||||
public ModifyRenderContext(Func<Type, string, string, Func<string, string>, string> resolveColumn, Func<object, string> renderValue, Func<string, string> decorateName)
|
||||
public ModifyRenderContext(Func<Type, string, string, Func<string, string>, string> resolveColumn, Func<object, string> renderValue, Func<string, string> decorateName, Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
_resolveColumn = resolveColumn;
|
||||
_renderValue = renderValue;
|
||||
_decorateName = decorateName;
|
||||
_renderSubQuery = renderSubQuery;
|
||||
}
|
||||
|
||||
public string ResolveColumn(Type modelType, string memberName, string alias)
|
||||
@@ -43,6 +46,11 @@ namespace DynamORM.Builders.Implementation
|
||||
{
|
||||
return _decorateName(name);
|
||||
}
|
||||
|
||||
public string RenderSubQuery(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return _renderSubQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMappedColumn<T, TValue>(Expression<Func<T, TValue>> selector)
|
||||
@@ -67,12 +75,13 @@ namespace DynamORM.Builders.Implementation
|
||||
Func<TypedTableContext<T>, TypedSqlPredicate> predicate,
|
||||
string alias,
|
||||
Func<object, string> renderValue,
|
||||
Func<string, string> decorateName)
|
||||
Func<string, string> decorateName,
|
||||
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName);
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
|
||||
return predicate(new TypedTableContext<T>(alias)).Render(context);
|
||||
}
|
||||
|
||||
@@ -80,12 +89,13 @@ namespace DynamORM.Builders.Implementation
|
||||
Func<TypedTableContext<T>, TypedSqlExpression> expression,
|
||||
string alias,
|
||||
Func<object, string> renderValue,
|
||||
Func<string, string> decorateName)
|
||||
Func<string, string> decorateName,
|
||||
Func<IDynamicSelectQueryBuilder, string> renderSubQuery)
|
||||
{
|
||||
if (expression == null)
|
||||
throw new ArgumentNullException("expression");
|
||||
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName);
|
||||
ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery);
|
||||
return expression(new TypedTableContext<T>(alias)).Render(context);
|
||||
}
|
||||
|
||||
@@ -158,7 +168,7 @@ namespace DynamORM.Builders.Implementation
|
||||
: string.Format("{0}.{1}", alias, decorateName(mapped));
|
||||
}
|
||||
|
||||
private static string GetMappedColumnByName(Type modelType, string memberName)
|
||||
internal static string GetMappedColumnByName(Type modelType, string memberName)
|
||||
{
|
||||
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType);
|
||||
if (mapper == null)
|
||||
|
||||
@@ -19,5 +19,8 @@ namespace DynamORM.TypedSql
|
||||
|
||||
/// <summary>Decorate SQL identifier.</summary>
|
||||
string DecorateName(string name);
|
||||
|
||||
/// <summary>Render subquery SQL and merge any parameters into current context.</summary>
|
||||
string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Builders;
|
||||
|
||||
namespace DynamORM.TypedSql
|
||||
{
|
||||
@@ -54,6 +55,84 @@ namespace DynamORM.TypedSql
|
||||
return Func<T>("COALESCE", expressions);
|
||||
}
|
||||
|
||||
/// <summary>Create SUM expression.</summary>
|
||||
public static TypedSqlExpression<T> Sum<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("SUM", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create AVG expression.</summary>
|
||||
public static TypedSqlExpression<T> Avg<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("AVG", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create MIN expression.</summary>
|
||||
public static TypedSqlExpression<T> Min<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("MIN", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create MAX expression.</summary>
|
||||
public static TypedSqlExpression<T> Max<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("MAX", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create ABS expression.</summary>
|
||||
public static TypedSqlExpression<T> Abs<T>(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<T>("ABS", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create UPPER expression.</summary>
|
||||
public static TypedSqlExpression<string> Upper(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("UPPER", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create LOWER expression.</summary>
|
||||
public static TypedSqlExpression<string> Lower(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("LOWER", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create TRIM expression.</summary>
|
||||
public static TypedSqlExpression<string> Trim(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<string>("TRIM", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create LENGTH expression.</summary>
|
||||
public static TypedSqlExpression<int> Length(TypedSqlExpression expression)
|
||||
{
|
||||
return Func<int>("LENGTH", expression);
|
||||
}
|
||||
|
||||
/// <summary>Create NULLIF expression.</summary>
|
||||
public static TypedSqlExpression<T> NullIf<T>(TypedSqlExpression left, TypedSqlExpression right)
|
||||
{
|
||||
return Func<T>("NULLIF", left, right);
|
||||
}
|
||||
|
||||
/// <summary>Create CURRENT_TIMESTAMP expression.</summary>
|
||||
public static TypedSqlExpression<DateTime> CurrentTimestamp()
|
||||
{
|
||||
return Raw<DateTime>("CURRENT_TIMESTAMP");
|
||||
}
|
||||
|
||||
/// <summary>Create scalar subquery expression.</summary>
|
||||
public static TypedSqlExpression<T> SubQuery<T>(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return new TypedSqlSubQueryExpression<T>(query);
|
||||
}
|
||||
|
||||
/// <summary>Create EXISTS predicate.</summary>
|
||||
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
return new TypedSqlExistsPredicate(query);
|
||||
}
|
||||
|
||||
/// <summary>Create CASE expression builder.</summary>
|
||||
public static TypedSqlCaseBuilder<T> Case<T>()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DynamORM.Builders;
|
||||
|
||||
namespace DynamORM.TypedSql
|
||||
{
|
||||
@@ -83,6 +85,30 @@ namespace DynamORM.TypedSql
|
||||
{
|
||||
return new TypedSqlUnaryPredicate(this, "IS NOT NULL");
|
||||
}
|
||||
|
||||
/// <summary>LIKE predicate.</summary>
|
||||
public TypedSqlPredicate Like(string pattern)
|
||||
{
|
||||
return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern));
|
||||
}
|
||||
|
||||
/// <summary>IN predicate.</summary>
|
||||
public TypedSqlPredicate In(params object[] values)
|
||||
{
|
||||
return new TypedSqlInPredicate(this, values);
|
||||
}
|
||||
|
||||
/// <summary>IN predicate.</summary>
|
||||
public TypedSqlPredicate In(IEnumerable values)
|
||||
{
|
||||
return new TypedSqlInPredicate(this, values);
|
||||
}
|
||||
|
||||
/// <summary>BETWEEN predicate.</summary>
|
||||
public TypedSqlPredicate Between(object lower, object upper)
|
||||
{
|
||||
return new TypedSqlBetweenPredicate(this, lower is TypedSqlExpression ? (TypedSqlExpression)lower : Sql.Val(lower), upper is TypedSqlExpression ? (TypedSqlExpression)upper : Sql.Val(upper));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Typed SQL expression.</summary>
|
||||
@@ -167,7 +193,7 @@ namespace DynamORM.TypedSql
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlValueExpression<T> : TypedSqlExpression<T>
|
||||
internal sealed class TypedSqlValueExpression<T> : TypedSqlExpression<T>, ITypedSqlNullValue
|
||||
{
|
||||
private readonly object _value;
|
||||
|
||||
@@ -180,6 +206,11 @@ namespace DynamORM.TypedSql
|
||||
{
|
||||
return context.RenderValue(_value);
|
||||
}
|
||||
|
||||
public bool IsNullValue
|
||||
{
|
||||
get { return _value == null; }
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlRawExpression<T> : TypedSqlExpression<T>
|
||||
@@ -251,13 +282,62 @@ namespace DynamORM.TypedSql
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
string op = _operator;
|
||||
if (_right is TypedSqlValueExpression<object> && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase))
|
||||
TypedSqlValueExpression<object> objRight = _right as TypedSqlValueExpression<object>;
|
||||
if ((objRight != null && objRight.IsNullValue) || (_right is ITypedSqlNullValue && ((ITypedSqlNullValue)_right).IsNullValue))
|
||||
op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator;
|
||||
|
||||
return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context));
|
||||
}
|
||||
}
|
||||
|
||||
internal interface ITypedSqlNullValue
|
||||
{
|
||||
bool IsNullValue { get; }
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlInPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlExpression _left;
|
||||
private readonly IEnumerable _values;
|
||||
|
||||
internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values)
|
||||
{
|
||||
_left = left;
|
||||
_values = values;
|
||||
}
|
||||
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
List<string> rendered = new List<string>();
|
||||
foreach (object value in _values)
|
||||
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
||||
|
||||
if (rendered.Count == 0)
|
||||
return "(1 = 0)";
|
||||
|
||||
return string.Format("({0} IN({1}))", _left.Render(context), string.Join(", ", rendered.ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlExpression _left;
|
||||
private readonly TypedSqlExpression _lower;
|
||||
private readonly TypedSqlExpression _upper;
|
||||
|
||||
internal TypedSqlBetweenPredicate(TypedSqlExpression left, TypedSqlExpression lower, TypedSqlExpression upper)
|
||||
{
|
||||
_left = left;
|
||||
_lower = lower;
|
||||
_upper = upper;
|
||||
}
|
||||
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("({0} BETWEEN {1} AND {2})", _left.Render(context), _lower.Render(context), _upper.Render(context));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly TypedSqlPredicate _left;
|
||||
@@ -318,4 +398,34 @@ namespace DynamORM.TypedSql
|
||||
return string.Join(" ", items.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlSubQueryExpression<T> : TypedSqlExpression<T>
|
||||
{
|
||||
private readonly IDynamicSelectQueryBuilder _query;
|
||||
|
||||
internal TypedSqlSubQueryExpression(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("({0})", context.RenderSubQuery(_query));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TypedSqlExistsPredicate : TypedSqlPredicate
|
||||
{
|
||||
private readonly IDynamicSelectQueryBuilder _query;
|
||||
|
||||
internal TypedSqlExistsPredicate(IDynamicSelectQueryBuilder query)
|
||||
{
|
||||
_query = query;
|
||||
}
|
||||
|
||||
internal override string Render(ITypedSqlRenderContext context)
|
||||
{
|
||||
return string.Format("(EXISTS ({0}))", context.RenderSubQuery(_query));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user