Extend typed SQL DSL with pattern helpers and typed subqueries
This commit is contained in:
@@ -15915,11 +15915,31 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
return new TypedSqlSubQueryExpression<T>(query);
|
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>
|
/// <summary>Create EXISTS predicate.</summary>
|
||||||
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
||||||
{
|
{
|
||||||
return new TypedSqlExistsPredicate(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>
|
/// <summary>Create CASE expression builder.</summary>
|
||||||
public static TypedSqlCaseBuilder<T> Case<T>()
|
public static TypedSqlCaseBuilder<T> Case<T>()
|
||||||
{
|
{
|
||||||
@@ -16029,11 +16049,36 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
return new TypedSqlInPredicate(this, values);
|
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>
|
/// <summary>BETWEEN predicate.</summary>
|
||||||
public TypedSqlPredicate Between(object lower, object upper)
|
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));
|
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>
|
/// <summary>Typed SQL expression.</summary>
|
||||||
public abstract class TypedSqlExpression<T> : TypedSqlExpression
|
public abstract class TypedSqlExpression<T> : TypedSqlExpression
|
||||||
@@ -16201,11 +16246,13 @@ namespace DynamORM
|
|||||||
{
|
{
|
||||||
private readonly TypedSqlExpression _left;
|
private readonly TypedSqlExpression _left;
|
||||||
private readonly IEnumerable _values;
|
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;
|
_left = left;
|
||||||
_values = values;
|
_values = values;
|
||||||
|
_negated = negated;
|
||||||
}
|
}
|
||||||
internal override string Render(ITypedSqlRenderContext context)
|
internal override string Render(ITypedSqlRenderContext context)
|
||||||
{
|
{
|
||||||
@@ -16214,9 +16261,9 @@ namespace DynamORM
|
|||||||
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
||||||
|
|
||||||
if (rendered.Count == 0)
|
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
|
internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate
|
||||||
@@ -16343,6 +16390,11 @@ namespace DynamORM
|
|||||||
|
|
||||||
return new TypedSqlColumnExpression<TValue>(typeof(T), member.Member.Name, Alias);
|
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
|
namespace Validation
|
||||||
|
|||||||
@@ -130,6 +130,44 @@ namespace DynamORM.Tests.TypedSql
|
|||||||
NormalizeSql(cmd.CommandText()));
|
NormalizeSql(cmd.CommandText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestWhereSqlSupportsNotIn()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.WhereSql(u => u.Col(x => x.Id).NotIn(1, 2))
|
||||||
|
.SelectSql(u => u.Col(x => x.Id));
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" NOT IN([$0], [$1]))",
|
||||||
|
NormalizeSql(cmd.CommandText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestWhereSqlSupportsEmptyNotIn()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.WhereSql(u => u.Col(x => x.Id).NotIn(new int[0]))
|
||||||
|
.SelectSql(u => u.Col(x => x.Id));
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
"SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (1 = 1)",
|
||||||
|
NormalizeSql(cmd.CommandText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestWhereSqlSupportsPatternHelpers()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.WhereSql(u => u.Col(x => x.Code).StartsWith("AB")
|
||||||
|
.And(u.Col(x => x.Code).EndsWith("YZ"))
|
||||||
|
.And(u.Col(x => x.Code).Contains("MID")))
|
||||||
|
.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.\"user_code\" LIKE [$1])) AND (u.\"user_code\" LIKE [$2]))",
|
||||||
|
NormalizeSql(cmd.CommandText()));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestWhereSqlSupportsEmptyIn()
|
public void TestWhereSqlSupportsEmptyIn()
|
||||||
{
|
{
|
||||||
@@ -175,6 +213,17 @@ namespace DynamORM.Tests.TypedSql
|
|||||||
NormalizeSql(cmd.CommandText()));
|
NormalizeSql(cmd.CommandText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectSqlSupportsWildcardAll()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.SelectSql(u => u.All());
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
"SELECT u.* FROM \"sample_users\" AS u",
|
||||||
|
NormalizeSql(cmd.CommandText()));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSelectSqlSupportsScalarSubQuery()
|
public void TestSelectSqlSupportsScalarSubQuery()
|
||||||
{
|
{
|
||||||
@@ -206,6 +255,39 @@ namespace DynamORM.Tests.TypedSql
|
|||||||
NormalizeSql(cmd.CommandText()));
|
NormalizeSql(cmd.CommandText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectSqlSupportsTypedSubQueryHelper()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.SelectSql(u => Sql.SubQuery<TypedFluentUser, long>(
|
||||||
|
Database,
|
||||||
|
sq => sq
|
||||||
|
.SelectSql(x => x.Col(a => a.Id))
|
||||||
|
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
|
||||||
|
"x").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 TestWhereSqlSupportsTypedExistsHelper()
|
||||||
|
{
|
||||||
|
var cmd = Database.FromTyped<TypedFluentUser>("u")
|
||||||
|
.WhereSql(u => Sql.Exists<TypedFluentUser>(
|
||||||
|
Database,
|
||||||
|
sq => sq
|
||||||
|
.SelectSql(x => x.Col(a => a.Id))
|
||||||
|
.WhereSql(x => x.Col(a => a.Code).Eq("A")),
|
||||||
|
"x"))
|
||||||
|
.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]
|
[Test]
|
||||||
public void TestInsertSqlObjectProjection()
|
public void TestInsertSqlObjectProjection()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -127,12 +127,34 @@ namespace DynamORM.TypedSql
|
|||||||
return new TypedSqlSubQueryExpression<T>(query);
|
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>
|
/// <summary>Create EXISTS predicate.</summary>
|
||||||
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query)
|
||||||
{
|
{
|
||||||
return new TypedSqlExistsPredicate(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>
|
/// <summary>Create CASE expression builder.</summary>
|
||||||
public static TypedSqlCaseBuilder<T> Case<T>()
|
public static TypedSqlCaseBuilder<T> Case<T>()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -104,11 +104,41 @@ namespace DynamORM.TypedSql
|
|||||||
return new TypedSqlInPredicate(this, values);
|
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>
|
/// <summary>BETWEEN predicate.</summary>
|
||||||
public TypedSqlPredicate Between(object lower, object upper)
|
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));
|
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>
|
/// <summary>Typed SQL expression.</summary>
|
||||||
@@ -299,11 +329,13 @@ namespace DynamORM.TypedSql
|
|||||||
{
|
{
|
||||||
private readonly TypedSqlExpression _left;
|
private readonly TypedSqlExpression _left;
|
||||||
private readonly IEnumerable _values;
|
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;
|
_left = left;
|
||||||
_values = values;
|
_values = values;
|
||||||
|
_negated = negated;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override string Render(ITypedSqlRenderContext context)
|
internal override string Render(ITypedSqlRenderContext context)
|
||||||
@@ -313,9 +345,9 @@ namespace DynamORM.TypedSql
|
|||||||
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context));
|
||||||
|
|
||||||
if (rendered.Count == 0)
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,5 +36,11 @@ namespace DynamORM.TypedSql
|
|||||||
|
|
||||||
return new TypedSqlColumnExpression<TValue>(typeof(T), member.Member.Name, Alias);
|
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 + ".*");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user