Extend typed SQL DSL with pattern helpers and typed subqueries

This commit is contained in:
root
2026-02-27 08:36:47 +01:00
parent 97ab4c1e15
commit f293bd95c6
5 changed files with 200 additions and 6 deletions

View File

@@ -15915,11 +15915,31 @@ namespace DynamORM
{
return new TypedSqlSubQueryExpression<T>(query);
}
/// <summary>Create scalar typed subquery expression without manually constructing the builder.</summary>
public static TypedSqlExpression<TResult> SubQuery<TModel, TResult>(DynamicDatabase db, Func<IDynamicTypedSelectQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlSubQueryExpression<TResult>(configure(db.FromTyped<TModel>(alias, noLock)));
}
/// <summary>Create EXISTS predicate.</summary>
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
{
return new TypedSqlExistsPredicate(query);
}
/// <summary>Create EXISTS predicate from typed subquery factory.</summary>
public static TypedSqlPredicate Exists<TModel>(DynamicDatabase db, Func<IDynamicTypedSelectQueryBuilder<TModel>, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false)
{
if (db == null)
throw new ArgumentNullException("db");
if (configure == null)
throw new ArgumentNullException("configure");
return new TypedSqlExistsPredicate(configure(db.FromTyped<TModel>(alias, noLock)));
}
/// <summary>Create CASE expression builder.</summary>
public static TypedSqlCaseBuilder<T> Case<T>()
{
@@ -16029,11 +16049,36 @@ namespace DynamORM
{
return new TypedSqlInPredicate(this, values);
}
/// <summary>NOT IN predicate.</summary>
public TypedSqlPredicate NotIn(params object[] values)
{
return new TypedSqlInPredicate(this, values, true);
}
/// <summary>NOT IN predicate.</summary>
public TypedSqlPredicate NotIn(IEnumerable values)
{
return new TypedSqlInPredicate(this, values, true);
}
/// <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>Starts-with LIKE predicate.</summary>
public TypedSqlPredicate StartsWith(string value)
{
return Like((value ?? string.Empty) + "%");
}
/// <summary>Ends-with LIKE predicate.</summary>
public TypedSqlPredicate EndsWith(string value)
{
return Like("%" + (value ?? string.Empty));
}
/// <summary>Contains LIKE predicate.</summary>
public TypedSqlPredicate Contains(string value)
{
return Like("%" + (value ?? string.Empty) + "%");
}
}
/// <summary>Typed SQL expression.</summary>
public abstract class TypedSqlExpression<T> : TypedSqlExpression
@@ -16201,11 +16246,13 @@ namespace DynamORM
{
private readonly TypedSqlExpression _left;
private readonly IEnumerable _values;
private readonly bool _negated;
internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values)
internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values, bool negated = false)
{
_left = left;
_values = values;
_negated = negated;
}
internal override string Render(ITypedSqlRenderContext context)
{
@@ -16214,9 +16261,9 @@ namespace DynamORM
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
if (rendered.Count == 0)
return "(1 = 0)";
return _negated ? "(1 = 1)" : "(1 = 0)";
return string.Format("({0} IN({1}))", _left.Render(context), string.Join(", ", rendered.ToArray()));
return string.Format("({0} {1}({2}))", _left.Render(context), _negated ? "NOT IN" : "IN", string.Join(", ", rendered.ToArray()));
}
}
internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate
@@ -16343,6 +16390,11 @@ namespace DynamORM
return new TypedSqlColumnExpression<TValue>(typeof(T), member.Member.Name, Alias);
}
/// <summary>Select all columns from this typed table context.</summary>
public TypedSqlExpression<object> All()
{
return Sql.Raw<object>(string.IsNullOrEmpty(Alias) ? "*" : Alias + ".*");
}
}
}
namespace Validation