From 5bdccedf42e0bb1910fd0e34a07fc9cfa11a4816 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 16:07:23 +0100 Subject: [PATCH 01/20] Explore typed fluent mapped properties with aliases and order markers --- AmalgamationTool/DynamORM.Amalgamation.cs | 522 +++++++++++++++++- DynamORM.Tests/Helpers/TypedFluentUser.cs | 42 ++ .../Select/TypedFluentBuilderTests.cs | 99 ++++ .../IDynamicTypedSelectQueryBuilder.cs | 69 +++ .../DynamicSelectQueryBuilder.cs | 4 +- .../DynamicTypedSelectQueryBuilder.cs | 517 +++++++++++++++++ DynamORM/Builders/TypedFluentExtensions.cs | 79 +++ DynamORM/DynamicDatabase.cs | 40 +- 8 files changed, 1347 insertions(+), 25 deletions(-) create mode 100644 DynamORM.Tests/Helpers/TypedFluentUser.cs create mode 100644 DynamORM.Tests/Select/TypedFluentBuilderTests.cs create mode 100644 DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs create mode 100644 DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs create mode 100644 DynamORM/Builders/TypedFluentExtensions.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index e9ebcd5..a59403d 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -1957,23 +1957,26 @@ namespace DynamORM /// Table alias. /// use no lock. /// This instance to permit chaining. - public virtual IDynamicSelectQueryBuilder From(string alias = null, bool noLock = false) + public virtual IDynamicTypedSelectQueryBuilder From(string alias = null, bool noLock = false) { // TODO: Make it more readable and maitainable + DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); + if (noLock) { if (string.IsNullOrEmpty(alias)) - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).NoLock()); + builder.From(x => x(typeof(T)).NoLock()); else - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias).NoLock()); + builder.From(x => x(typeof(T)).As(alias).NoLock()); } else { if (string.IsNullOrEmpty(alias)) - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T))); + builder.From(x => x(typeof(T))); else - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); + builder.From(x => x(typeof(T)).As(alias)); } + return builder; } /// Adds to the FROM clause using . /// Type which can be represented in database. @@ -7161,6 +7164,41 @@ namespace DynamORM #endregion Top/Limit/Offset/Distinct } + /// Typed select query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedSelectQueryBuilder : IDynamicSelectQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Where(Expression> predicate); + + /// Add typed having predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Having(Expression> predicate); + + /// Add typed selected columns using mapped properties. + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors); + + /// Add typed group by columns using mapped properties. + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors); + + /// Add typed order by columns using mapped properties. Supports Asc()/Desc(). + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); + } /// Dynamic update query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder @@ -7290,6 +7328,49 @@ namespace DynamORM /// Gets table schema. Dictionary Schema { get; } } + /// Marker extensions for typed fluent builder expressions. + public static class TypedFluentExtensions + { + /// Typed select helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder SelectTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.Select(selector, selectors); + } + /// Typed group by helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder GroupByTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.GroupBy(selector, selectors); + } + /// Typed order by helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder OrderByTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.OrderBy(selector, selectors); + } + /// Marks select projection alias in typed expressions. + public static T As(this T source, string alias) + { + return source; + } + /// Marks ascending order in typed order expressions. + public static T Asc(this T source) + { + return source; + } + /// Marks descending order in typed order expressions. + public static T Desc(this T source) + { + return source; + } + } namespace Extensions { internal static class DynamicHavingQueryExtensions @@ -10304,6 +10385,437 @@ namespace DynamORM } #endregion IExtendedDisposable } + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder + { + private readonly DynamicTypeMap _mapper; + + public DynamicTypedSelectQueryBuilder(DynamicDatabase db) + : base(db) + { + _mapper = DynamicMapperCache.GetMapper() + ?? throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(T).FullName)); + } + public IDynamicTypedSelectQueryBuilder Where(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = ParseTypedCondition(predicate.Body); + + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) + { + base.Join(func); + return this; + } + public new IDynamicTypedSelectQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + public new IDynamicTypedSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + public new IDynamicTypedSelectQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + public new IDynamicTypedSelectQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + public IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddSelectSelector(s); + } + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddGroupBySelector(s); + } + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddOrderBySelector(s); + } + return this; + } + public new IDynamicTypedSelectQueryBuilder Top(int? top) + { + base.Top(top); + return this; + } + public new IDynamicTypedSelectQueryBuilder Limit(int? limit) + { + base.Limit(limit); + return this; + } + public new IDynamicTypedSelectQueryBuilder Offset(int? offset) + { + base.Offset(offset); + return this; + } + public new IDynamicTypedSelectQueryBuilder Distinct(bool distinct = true) + { + base.Distinct(distinct); + return this; + } + public IDynamicTypedSelectQueryBuilder Having(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = ParseTypedCondition(predicate.Body); + + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) + { + base.Having(column); + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) + { + base.Having(column, op, value); + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(string column, object value) + { + base.Having(column, value); + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(object conditions, bool schema = false) + { + base.Having(conditions, schema); + return this; + } + private void AddSelectSelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + + if (body is NewExpression ne) + { + foreach (var argument in ne.Arguments) + { + var parsed = ParseTypedSelectExpression(argument); + ((IDynamicSelectQueryBuilder)this).Select(x => parsed); + } + } + else + { + var parsed = ParseTypedSelectExpression(body); + ((IDynamicSelectQueryBuilder)this).Select(x => parsed); + } + } + private void AddGroupBySelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + + if (body is NewExpression ne) + { + foreach (var argument in ne.Arguments) + { + var parsed = ParseTypedMemberAccess(argument); + ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); + } + } + else + { + var parsed = ParseTypedMemberAccess(body); + ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); + } + } + private void AddOrderBySelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + bool ascending = true; + + if (body is MethodCallExpression call && IsAscOrDesc(call)) + { + ascending = call.Method.Name.ToUpper() != "DESC"; + body = UnwrapConvert(call.Object ?? call.Arguments.FirstOrDefault()); + } + string main = ParseTypedMemberAccess(body); + string parsed = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC"); + ((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed); + } + private string ParseTypedCondition(Expression expression) + { + expression = UnwrapConvert(expression); + + if (expression is BinaryExpression binary) + { + switch (binary.NodeType) + { + case ExpressionType.AndAlso: + case ExpressionType.And: + return string.Format("({0} AND {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); + case ExpressionType.OrElse: + case ExpressionType.Or: + return string.Format("({0} OR {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + { + DynamicSchemaColumn? columnSchema = null; + string left = ParseTypedValue(binary.Left, ref columnSchema); + string right = ParseTypedValue(binary.Right, ref columnSchema); + string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); + return string.Format("({0} {1} {2})", left, op, right); + } + } + } + if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) + return string.Format("(NOT {0})", ParseTypedCondition(unary.Operand)); + + if (expression is MethodCallExpression call && IsEnumerableContains(call)) + { + DynamicSchemaColumn? col = null; + return ParseTypedContains(call, ref col); + } + throw new NotSupportedException(string.Format("Typed fluent where expression is not supported: {0}", expression)); + } + private string ParseTypedValue(Expression expression, ref DynamicSchemaColumn? columnSchema) + { + expression = UnwrapConvert(expression); + + if (IsMemberFromTypedParameter(expression)) + { + string col = ParseTypedMemberAccess(expression); + columnSchema = GetColumnFromSchema(col); + return col; + } + if (expression is MethodCallExpression call && IsEnumerableContains(call)) + return ParseTypedContains(call, ref columnSchema); + + object value = EvaluateExpression(expression); + return ParseConstant(value, Parameters, columnSchema); + } + private string ParseTypedContains(MethodCallExpression call, ref DynamicSchemaColumn? columnSchema) + { + // Supports: list.Contains(x.Property) and Enumerable.Contains(list, x.Property) + Expression collection; + Expression candidate; + + if (call.Object != null) + { + collection = call.Object; + candidate = call.Arguments[0]; + } + else + { + collection = call.Arguments[0]; + candidate = call.Arguments[1]; + } + candidate = UnwrapConvert(candidate); + if (!IsMemberFromTypedParameter(candidate)) + throw new NotSupportedException(string.Format("Typed Contains() must target a mapped member: {0}", call)); + + string left = ParseTypedMemberAccess(candidate); + columnSchema = GetColumnFromSchema(left); + + var values = EvaluateExpression(collection) as IEnumerable; + if (values == null && collection is MethodCallExpression implicitCall && + string.Equals(implicitCall.Method.Name, "op_Implicit", StringComparison.Ordinal) && + implicitCall.Arguments.Count > 0) + { + values = EvaluateExpression(implicitCall.Arguments[0]) as IEnumerable; + } + if (values == null) + throw new NotSupportedException(string.Format("Typed Contains() source is not enumerable: {0}", call)); + + var inList = new List(); + foreach (var item in values.Cast()) + inList.Add(ParseConstant(item, Parameters, columnSchema)); + + if (!inList.Any()) + return "(1 = 0)"; + + return string.Format("({0} IN({1}))", left, string.Join(", ", inList)); + } + private string ParseTypedMemberAccess(Expression expression) + { + expression = UnwrapConvert(expression); + if (!(expression is MemberExpression member) || !IsMemberFromTypedParameter(member)) + throw new NotSupportedException(string.Format("Typed fluent member access is not supported: {0}", expression)); + + string mappedColumn = null; + var property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + var colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + if (string.IsNullOrEmpty(mappedColumn)) + { + mappedColumn = _mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? _mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? _mapper.ColumnsMap + .Where(x => string.Equals(x.Value.Name, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Key) + .FirstOrDefault() + ?? member.Member.Name; + } + string tablePrefix = GetRootAliasOrTableName(); + + string qualified = string.IsNullOrEmpty(tablePrefix) + ? mappedColumn + : string.Format("{0}.{1}", tablePrefix, mappedColumn); + + return FixObjectName(qualified); + } + private string ParseTypedSelectExpression(Expression expression) + { + expression = UnwrapConvert(expression); + + if (expression is MethodCallExpression call && IsAsCall(call)) + { + string left = ParseTypedMemberAccess(call.Object ?? call.Arguments.FirstOrDefault()); + var alias = EvaluateExpression(call.Arguments.Last()) == null ? null : EvaluateExpression(call.Arguments.Last()).ToString(); + alias = alias.Validated("Alias"); + + return string.Format("{0} AS {1}", left, Database.DecorateName(alias)); + } + return ParseTypedMemberAccess(expression); + } + private string GetRootAliasOrTableName() + { + var mappedTable = _mapper.Table == null || string.IsNullOrEmpty(_mapper.Table.Name) + ? _mapper.Type.Name + : _mapper.Table.Name; + + var table = Tables.FirstOrDefault(t => t.Name == mappedTable || t.Name == Database.StripName(mappedTable)); + if (table == null) + table = Tables.FirstOrDefault(); + + if (table == null) + return null; + + return string.IsNullOrEmpty(table.Alias) ? table.Name : table.Alias; + } + private static bool IsMemberFromTypedParameter(Expression expression) + { + var member = expression as MemberExpression; + if (member == null) + return false; + + var parameter = member.Expression as ParameterExpression; + return parameter != null && parameter.Type == typeof(T); + } + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression unary && + (unary.NodeType == ExpressionType.Convert || unary.NodeType == ExpressionType.ConvertChecked)) + expression = unary.Operand; + + return expression; + } + private static bool IsNullConstant(Expression expression) + { + expression = UnwrapConvert(expression); + return expression is ConstantExpression constant && constant.Value == null; + } + private static string GetBinaryOperator(ExpressionType type, bool rightIsNull) + { + switch (type) + { + case ExpressionType.Equal: return rightIsNull ? "IS" : "="; + case ExpressionType.NotEqual: return rightIsNull ? "IS NOT" : "<>"; + case ExpressionType.GreaterThan: return ">"; + case ExpressionType.GreaterThanOrEqual: return ">="; + case ExpressionType.LessThan: return "<"; + case ExpressionType.LessThanOrEqual: return "<="; + default: throw new NotSupportedException(string.Format("Expression operation is not supported: {0}", type)); + } + } + private static bool IsEnumerableContains(MethodCallExpression call) + { + if (!string.Equals(call.Method.Name, "Contains", StringComparison.Ordinal)) + return false; + + if (call.Object != null && call.Arguments.Count == 1) + return true; + + return call.Object == null && call.Arguments.Count == 2; + } + private static bool IsAsCall(MethodCallExpression call) + { + return string.Equals(call.Method.Name, "As", StringComparison.Ordinal) + && (call.Arguments.Count == 1 || call.Arguments.Count == 2); + } + private static bool IsAscOrDesc(MethodCallExpression call) + { + string name = call.Method.Name.ToUpper(); + return (name == "ASC" || name == "DESC") + && (call.Arguments.Count == 0 || call.Arguments.Count == 1); + } + private static object EvaluateExpression(Expression expression) + { + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { diff --git a/DynamORM.Tests/Helpers/TypedFluentUser.cs b/DynamORM.Tests/Helpers/TypedFluentUser.cs new file mode 100644 index 0000000..52cd8da --- /dev/null +++ b/DynamORM.Tests/Helpers/TypedFluentUser.cs @@ -0,0 +1,42 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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 DynamORM.Mapper; + +namespace DynamORM.Tests.Helpers +{ + [Table(Name = "sample_users")] + public class TypedFluentUser + { + [Column("id_user", true)] + public long Id { get; set; } + + [Column("user_code")] + public string Code { get; set; } + } +} diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs new file mode 100644 index 0000000..0d83954 --- /dev/null +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -0,0 +1,99 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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.Linq; +using DynamORM.Builders; +using DynamORM.Tests.Helpers; +using NUnit.Framework; + +namespace DynamORM.Tests.Select +{ + [TestFixture] + public class TypedFluentBuilderTests : TestsBase + { + [SetUp] + public void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset); + } + + [TearDown] + public void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + [Test] + public void TestTypedWhereAndSelectUsesPropertyMap() + { + var cmd = Database.From("u") + .Where(u => u.Id == 1) + .SelectTyped(u => u.Code.As("CodeAlias")) + .OrderByTyped(u => u.Code.Desc()); + + Assert.AreEqual( + string.Format("SELECT u.\"user_code\" AS \"CodeAlias\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" = [${0}]) ORDER BY u.\"user_code\" DESC", cmd.Parameters.Keys.First()), + cmd.CommandText()); + } + + [Test] + public void TestTypedWhereSupportsContains() + { + var ids = new[] { 1L, 2L, 3L }.ToList(); + + var cmd = Database.From("u") + .Where(u => ids.Contains(u.Id)) + .SelectTyped(u => u.Id); + + Assert.AreEqual( + string.Format("SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" IN([${0}], [${1}], [${2}]))", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1), + cmd.Parameters.Keys.ElementAt(2)), + cmd.CommandText()); + } + + [Test] + public void TestTypedGroupByHavingOrderBy() + { + var cmd = Database.From("u") + .SelectTyped(u => u.Code) + .GroupByTyped(u => u.Code) + .Having(u => u.Code != null) + .OrderByTyped(u => u.Code.Asc()); + + Assert.AreEqual("SELECT u.\"user_code\" FROM \"sample_users\" AS u GROUP BY u.\"user_code\" HAVING (u.\"user_code\" IS NOT NULL) ORDER BY u.\"user_code\" ASC", + cmd.CommandText()); + } + } +} diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs new file mode 100644 index 0000000..2a7be96 --- /dev/null +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -0,0 +1,69 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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.Linq.Expressions; + +namespace DynamORM.Builders +{ + /// Typed select query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedSelectQueryBuilder : IDynamicSelectQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Where(Expression> predicate); + + /// Add typed having predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Having(Expression> predicate); + + /// Add typed selected columns using mapped properties. + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors); + + /// Add typed group by columns using mapped properties. + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors); + + /// Add typed order by columns using mapped properties. Supports Asc()/Desc(). + /// Projection type. + /// Selector to parse. + /// Additional selectors to parse. + /// Builder instance. + IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); + } +} diff --git a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs index 5bc86d3..387f54b 100644 --- a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs @@ -42,7 +42,7 @@ using DynamORM.Mapper; namespace DynamORM.Builders.Implementation { /// Implementation of dynamic select query builder. - internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving + internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving { private int? _limit = null; private int? _offset = null; @@ -1452,4 +1452,4 @@ namespace DynamORM.Builders.Implementation #endregion IExtendedDisposable } -} \ No newline at end of file +} diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs new file mode 100644 index 0000000..b615a93 --- /dev/null +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -0,0 +1,517 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using DynamORM.Helpers; +using DynamORM.Mapper; + +namespace DynamORM.Builders.Implementation +{ + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder + { + private readonly DynamicTypeMap _mapper; + + public DynamicTypedSelectQueryBuilder(DynamicDatabase db) + : base(db) + { + _mapper = DynamicMapperCache.GetMapper() + ?? throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(T).FullName)); + } + + public IDynamicTypedSelectQueryBuilder Where(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = ParseTypedCondition(predicate.Body); + + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + + public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) + { + base.Join(func); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + + public IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddSelectSelector(s); + } + + return this; + } + + public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddGroupBySelector(s); + } + + return this; + } + + public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySelector(selector); + + if (selectors != null) + foreach (var s in selectors) + { + if (s == null) + throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); + + AddOrderBySelector(s); + } + + return this; + } + + public new IDynamicTypedSelectQueryBuilder Top(int? top) + { + base.Top(top); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Limit(int? limit) + { + base.Limit(limit); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Offset(int? offset) + { + base.Offset(offset); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Distinct(bool distinct = true) + { + base.Distinct(distinct); + return this; + } + + public IDynamicTypedSelectQueryBuilder Having(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = ParseTypedCondition(predicate.Body); + + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + + return this; + } + + public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) + { + base.Having(column); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) + { + base.Having(column, op, value); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Having(string column, object value) + { + base.Having(column, value); + return this; + } + + public new IDynamicTypedSelectQueryBuilder Having(object conditions, bool schema = false) + { + base.Having(conditions, schema); + return this; + } + + private void AddSelectSelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + + if (body is NewExpression ne) + { + foreach (var argument in ne.Arguments) + { + var parsed = ParseTypedSelectExpression(argument); + ((IDynamicSelectQueryBuilder)this).Select(x => parsed); + } + } + else + { + var parsed = ParseTypedSelectExpression(body); + ((IDynamicSelectQueryBuilder)this).Select(x => parsed); + } + } + + private void AddGroupBySelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + + if (body is NewExpression ne) + { + foreach (var argument in ne.Arguments) + { + var parsed = ParseTypedMemberAccess(argument); + ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); + } + } + else + { + var parsed = ParseTypedMemberAccess(body); + ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); + } + } + + private void AddOrderBySelector(Expression> selector) + { + var body = UnwrapConvert(selector.Body); + bool ascending = true; + + if (body is MethodCallExpression call && IsAscOrDesc(call)) + { + ascending = call.Method.Name.ToUpper() != "DESC"; + body = UnwrapConvert(call.Object ?? call.Arguments.FirstOrDefault()); + } + + string main = ParseTypedMemberAccess(body); + string parsed = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC"); + ((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed); + } + + private string ParseTypedCondition(Expression expression) + { + expression = UnwrapConvert(expression); + + if (expression is BinaryExpression binary) + { + switch (binary.NodeType) + { + case ExpressionType.AndAlso: + case ExpressionType.And: + return string.Format("({0} AND {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); + case ExpressionType.OrElse: + case ExpressionType.Or: + return string.Format("({0} OR {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + { + DynamicSchemaColumn? columnSchema = null; + string left = ParseTypedValue(binary.Left, ref columnSchema); + string right = ParseTypedValue(binary.Right, ref columnSchema); + string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); + return string.Format("({0} {1} {2})", left, op, right); + } + } + } + + if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) + return string.Format("(NOT {0})", ParseTypedCondition(unary.Operand)); + + if (expression is MethodCallExpression call && IsEnumerableContains(call)) + { + DynamicSchemaColumn? col = null; + return ParseTypedContains(call, ref col); + } + + throw new NotSupportedException(string.Format("Typed fluent where expression is not supported: {0}", expression)); + } + + private string ParseTypedValue(Expression expression, ref DynamicSchemaColumn? columnSchema) + { + expression = UnwrapConvert(expression); + + if (IsMemberFromTypedParameter(expression)) + { + string col = ParseTypedMemberAccess(expression); + columnSchema = GetColumnFromSchema(col); + return col; + } + + if (expression is MethodCallExpression call && IsEnumerableContains(call)) + return ParseTypedContains(call, ref columnSchema); + + object value = EvaluateExpression(expression); + return ParseConstant(value, Parameters, columnSchema); + } + + private string ParseTypedContains(MethodCallExpression call, ref DynamicSchemaColumn? columnSchema) + { + // Supports: list.Contains(x.Property) and Enumerable.Contains(list, x.Property) + Expression collection; + Expression candidate; + + if (call.Object != null) + { + collection = call.Object; + candidate = call.Arguments[0]; + } + else + { + collection = call.Arguments[0]; + candidate = call.Arguments[1]; + } + + candidate = UnwrapConvert(candidate); + if (!IsMemberFromTypedParameter(candidate)) + throw new NotSupportedException(string.Format("Typed Contains() must target a mapped member: {0}", call)); + + string left = ParseTypedMemberAccess(candidate); + columnSchema = GetColumnFromSchema(left); + + var values = EvaluateExpression(collection) as IEnumerable; + if (values == null && collection is MethodCallExpression implicitCall && + string.Equals(implicitCall.Method.Name, "op_Implicit", StringComparison.Ordinal) && + implicitCall.Arguments.Count > 0) + { + values = EvaluateExpression(implicitCall.Arguments[0]) as IEnumerable; + } + + if (values == null) + throw new NotSupportedException(string.Format("Typed Contains() source is not enumerable: {0}", call)); + + var inList = new List(); + foreach (var item in values.Cast()) + inList.Add(ParseConstant(item, Parameters, columnSchema)); + + if (!inList.Any()) + return "(1 = 0)"; + + return string.Format("({0} IN({1}))", left, string.Join(", ", inList)); + } + + private string ParseTypedMemberAccess(Expression expression) + { + expression = UnwrapConvert(expression); + if (!(expression is MemberExpression member) || !IsMemberFromTypedParameter(member)) + throw new NotSupportedException(string.Format("Typed fluent member access is not supported: {0}", expression)); + + string mappedColumn = null; + var property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + var colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + + if (string.IsNullOrEmpty(mappedColumn)) + { + mappedColumn = _mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? _mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? _mapper.ColumnsMap + .Where(x => string.Equals(x.Value.Name, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Key) + .FirstOrDefault() + ?? member.Member.Name; + } + string tablePrefix = GetRootAliasOrTableName(); + + string qualified = string.IsNullOrEmpty(tablePrefix) + ? mappedColumn + : string.Format("{0}.{1}", tablePrefix, mappedColumn); + + return FixObjectName(qualified); + } + + private string ParseTypedSelectExpression(Expression expression) + { + expression = UnwrapConvert(expression); + + if (expression is MethodCallExpression call && IsAsCall(call)) + { + string left = ParseTypedMemberAccess(call.Object ?? call.Arguments.FirstOrDefault()); + var alias = EvaluateExpression(call.Arguments.Last()) == null ? null : EvaluateExpression(call.Arguments.Last()).ToString(); + alias = alias.Validated("Alias"); + + return string.Format("{0} AS {1}", left, Database.DecorateName(alias)); + } + + return ParseTypedMemberAccess(expression); + } + + private string GetRootAliasOrTableName() + { + var mappedTable = _mapper.Table == null || string.IsNullOrEmpty(_mapper.Table.Name) + ? _mapper.Type.Name + : _mapper.Table.Name; + + var table = Tables.FirstOrDefault(t => t.Name == mappedTable || t.Name == Database.StripName(mappedTable)); + if (table == null) + table = Tables.FirstOrDefault(); + + if (table == null) + return null; + + return string.IsNullOrEmpty(table.Alias) ? table.Name : table.Alias; + } + + private static bool IsMemberFromTypedParameter(Expression expression) + { + var member = expression as MemberExpression; + if (member == null) + return false; + + var parameter = member.Expression as ParameterExpression; + return parameter != null && parameter.Type == typeof(T); + } + + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression unary && + (unary.NodeType == ExpressionType.Convert || unary.NodeType == ExpressionType.ConvertChecked)) + expression = unary.Operand; + + return expression; + } + + private static bool IsNullConstant(Expression expression) + { + expression = UnwrapConvert(expression); + return expression is ConstantExpression constant && constant.Value == null; + } + + private static string GetBinaryOperator(ExpressionType type, bool rightIsNull) + { + switch (type) + { + case ExpressionType.Equal: return rightIsNull ? "IS" : "="; + case ExpressionType.NotEqual: return rightIsNull ? "IS NOT" : "<>"; + case ExpressionType.GreaterThan: return ">"; + case ExpressionType.GreaterThanOrEqual: return ">="; + case ExpressionType.LessThan: return "<"; + case ExpressionType.LessThanOrEqual: return "<="; + default: throw new NotSupportedException(string.Format("Expression operation is not supported: {0}", type)); + } + } + + private static bool IsEnumerableContains(MethodCallExpression call) + { + if (!string.Equals(call.Method.Name, "Contains", StringComparison.Ordinal)) + return false; + + if (call.Object != null && call.Arguments.Count == 1) + return true; + + return call.Object == null && call.Arguments.Count == 2; + } + + private static bool IsAsCall(MethodCallExpression call) + { + return string.Equals(call.Method.Name, "As", StringComparison.Ordinal) + && (call.Arguments.Count == 1 || call.Arguments.Count == 2); + } + + private static bool IsAscOrDesc(MethodCallExpression call) + { + string name = call.Method.Name.ToUpper(); + return (name == "ASC" || name == "DESC") + && (call.Arguments.Count == 0 || call.Arguments.Count == 1); + } + + private static object EvaluateExpression(Expression expression) + { + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } +} diff --git a/DynamORM/Builders/TypedFluentExtensions.cs b/DynamORM/Builders/TypedFluentExtensions.cs new file mode 100644 index 0000000..a79f331 --- /dev/null +++ b/DynamORM/Builders/TypedFluentExtensions.cs @@ -0,0 +1,79 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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. +*/ + +namespace DynamORM.Builders +{ + /// Marker extensions for typed fluent builder expressions. + public static class TypedFluentExtensions + { + /// Typed select helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder SelectTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.Select(selector, selectors); + } + + /// Typed group by helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder GroupByTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.GroupBy(selector, selectors); + } + + /// Typed order by helper that avoids overload resolution with dynamic methods. + public static IDynamicTypedSelectQueryBuilder OrderByTyped( + this IDynamicTypedSelectQueryBuilder builder, + System.Linq.Expressions.Expression> selector, + params System.Linq.Expressions.Expression>[] selectors) + { + return builder.OrderBy(selector, selectors); + } + + /// Marks select projection alias in typed expressions. + public static T As(this T source, string alias) + { + return source; + } + + /// Marks ascending order in typed order expressions. + public static T Asc(this T source) + { + return source; + } + + /// Marks descending order in typed order expressions. + public static T Desc(this T source) + { + return source; + } + } +} diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index f627e4e..39695e1 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -453,24 +453,28 @@ namespace DynamORM /// Table alias. /// use no lock. /// This instance to permit chaining. - public virtual IDynamicSelectQueryBuilder From(string alias = null, bool noLock = false) - { - // TODO: Make it more readable and maitainable - if (noLock) - { - if (string.IsNullOrEmpty(alias)) - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).NoLock()); - else - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias).NoLock()); - } - else - { - if (string.IsNullOrEmpty(alias)) - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T))); - else - return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); - } - } + public virtual IDynamicTypedSelectQueryBuilder From(string alias = null, bool noLock = false) + { + // TODO: Make it more readable and maitainable + DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); + + if (noLock) + { + if (string.IsNullOrEmpty(alias)) + builder.From(x => x(typeof(T)).NoLock()); + else + builder.From(x => x(typeof(T)).As(alias).NoLock()); + } + else + { + if (string.IsNullOrEmpty(alias)) + builder.From(x => x(typeof(T))); + else + builder.From(x => x(typeof(T)).As(alias)); + } + + return builder; + } /// Adds to the FROM clause using . /// Type which can be represented in database. From 40a3907570b7303766d0c13c9712950ab8250906 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 16:23:41 +0100 Subject: [PATCH 02/20] Add typed modify and join fluent extensions --- AmalgamationTool/DynamORM.Amalgamation.cs | 198 ++++++++++++++++++ .../Modify/TypedModifyExtensionsTests.cs | 75 +++++++ .../Select/TypedFluentBuilderTests.cs | 11 + DynamORM/Builders/TypedJoinExtensions.cs | 86 ++++++++ DynamORM/Builders/TypedModifyExtensions.cs | 155 ++++++++++++++ 5 files changed, 525 insertions(+) create mode 100644 DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs create mode 100644 DynamORM/Builders/TypedJoinExtensions.cs create mode 100644 DynamORM/Builders/TypedModifyExtensions.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index a59403d..528d5f2 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7371,6 +7371,204 @@ namespace DynamORM return source; } } + /// Typed join helpers for typed select builder. + public static class TypedJoinExtensions + { + /// Add typed join on mapped members. Supports simple equality join expression only. + public static IDynamicTypedSelectQueryBuilder JoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + string alias, + Expression> on, + string joinType = "INNER JOIN") + { + if (builder == null) throw new ArgumentNullException("builder"); + if (on == null) throw new ArgumentNullException("on"); + + var rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = string.IsNullOrEmpty(rightMapper.Table.NullOr(t => t.Name)) + ? typeof(TRight).Name + : rightMapper.Table.Name; + + string rightOwner = rightMapper.Table.NullOr(t => t.Owner); + string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (builder.Tables.Count + 1).ToString() : alias; + + var be = on.Body as BinaryExpression; + if (be == null || be.NodeType != ExpressionType.Equal) + throw new NotSupportedException("JoinTyped currently supports only equality join expressions."); + + string leftPrefix = builder.Tables.FirstOrDefault().NullOr(t => string.IsNullOrEmpty(t.Alias) ? t.Name : t.Alias, null); + if (string.IsNullOrEmpty(leftPrefix)) + throw new InvalidOperationException("JoinTyped requires source table to be present."); + + string leftExpr = ResolveMappedSide(be.Left, typeof(TLeft), leftPrefix, builder.Database); + string rightExpr = ResolveMappedSide(be.Right, typeof(TRight), rightAlias, builder.Database); + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : builder.Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + builder.Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", joinType, rightTableExpr, rightAlias, leftExpr, rightExpr); + + builder.Join(x => joinExpr); + return builder; + } + private static string ResolveMappedSide(Expression expression, Type modelType, string prefix, DynamicDatabase db) + { + expression = UnwrapConvert(expression); + var member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException("Join side must be mapped member access."); + + var mapper = DynamicMapperCache.GetMapper(modelType); + string col = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", prefix, db.DecorateName(col)); + } + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + } + /// Typed helper extensions for update/insert/delete fluent APIs. + public static class TypedModifyExtensions + { + /// Add typed where predicate (AND-composed comparisons) for update builder. + public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) + { + AddTypedWhere(builder, predicate == null ? null : predicate.Body); + return builder; + } + /// Add typed where predicate (AND-composed comparisons) for delete builder. + public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) + { + AddTypedWhere(builder, predicate == null ? null : predicate.Body); + return builder; + } + /// Add typed value assignment for update builder. + public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) + { + string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); + return builder.Values(col, value); + } + /// Add typed value assignment for insert builder. + public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) + { + string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); + return builder.Insert(col, value); + } + /// Insert mapped object with compile-time type information. + public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class + { + return builder.Insert(value); + } + /// Update mapped object with compile-time type information. + public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class + { + return builder.Update(value); + } + private static void AddTypedWhere(dynamic builder, Expression expression) + { + if (expression == null) + throw new ArgumentNullException("predicate"); + + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + + if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) + { + AddTypedWhere(builder, be.Left); + AddTypedWhere(builder, be.Right); + return; + } + if (be != null) + { + string col = GetColumnName(GetRootParameterType(be.Left), be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + builder.Where(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + builder.Where(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + builder.Where(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + builder.Where(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + builder.Where(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + builder.Where(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + } + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + private static Type GetRootParameterType(Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression m = expression as MemberExpression; + if (m != null && m.Expression is ParameterExpression) + return ((ParameterExpression)m.Expression).Type; + + throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); + } + private static string GetColumnName(Type modelType, Expression expression) + { + if (modelType == null) + modelType = GetRootParameterType(expression); + + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + string col = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return col; + } + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } namespace Extensions { internal static class DynamicHavingQueryExtensions diff --git a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs new file mode 100644 index 0000000..a5390fa --- /dev/null +++ b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs @@ -0,0 +1,75 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System.Linq; +using DynamORM.Builders; +using DynamORM.Tests.Helpers; +using NUnit.Framework; + +namespace DynamORM.Tests.Modify +{ + [TestFixture] + public class TypedModifyExtensionsTests : TestsBase + { + [SetUp] + public void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset | + DynamicDatabaseOptions.SupportSchema); + } + + [TearDown] + public void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + [Test] + public void TestTypedUpdateSetAndWhere() + { + var cmd = Database.Update() + .SetTyped(u => u.Code, "777") + .WhereTyped(u => u.Id == 1 && u.Code == "1"); + + Assert.AreEqual( + string.Format("UPDATE \"sample_users\" SET \"code\" = [${0}] WHERE (\"id\" = [${1}]) AND (\"code\" = [${2}])", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1), + cmd.Parameters.Keys.ElementAt(2)), + cmd.CommandText()); + } + + [Test] + public void TestTypedDeleteWhere() + { + var cmd = Database.Delete() + .WhereTyped(u => u.Id == 2); + + Assert.AreEqual( + string.Format("DELETE FROM \"sample_users\" WHERE (\"id\" = [${0}])", cmd.Parameters.Keys.First()), + cmd.CommandText()); + } + + [Test] + public void TestTypedInsertColumns() + { + var cmd = Database.Insert() + .InsertTyped(u => u.Code, "900") + .InsertTyped(u => u.First, "Typed"); + + Assert.AreEqual( + string.Format("INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ([${0}], [${1}])", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1)), + cmd.CommandText()); + } + } +} diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index 0d83954..6528378 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -95,5 +95,16 @@ namespace DynamORM.Tests.Select Assert.AreEqual("SELECT u.\"user_code\" FROM \"sample_users\" AS u GROUP BY u.\"user_code\" HAVING (u.\"user_code\" IS NOT NULL) ORDER BY u.\"user_code\" ASC", cmd.CommandText()); } + + [Test] + public void TestTypedJoin() + { + var cmd = Database.From("u") + .JoinTyped("x", (l, r) => l.Id == r.Id) + .SelectTyped(u => u.Id); + + Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", + cmd.CommandText()); + } } } diff --git a/DynamORM/Builders/TypedJoinExtensions.cs b/DynamORM/Builders/TypedJoinExtensions.cs new file mode 100644 index 0000000..6ce4e27 --- /dev/null +++ b/DynamORM/Builders/TypedJoinExtensions.cs @@ -0,0 +1,86 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq; +using System.Linq.Expressions; +using DynamORM.Helpers; +using DynamORM.Mapper; + +namespace DynamORM.Builders +{ + /// Typed join helpers for typed select builder. + public static class TypedJoinExtensions + { + /// Add typed join on mapped members. Supports simple equality join expression only. + public static IDynamicTypedSelectQueryBuilder JoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + string alias, + Expression> on, + string joinType = "INNER JOIN") + { + if (builder == null) throw new ArgumentNullException("builder"); + if (on == null) throw new ArgumentNullException("on"); + + var rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = string.IsNullOrEmpty(rightMapper.Table.NullOr(t => t.Name)) + ? typeof(TRight).Name + : rightMapper.Table.Name; + + string rightOwner = rightMapper.Table.NullOr(t => t.Owner); + string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (builder.Tables.Count + 1).ToString() : alias; + + var be = on.Body as BinaryExpression; + if (be == null || be.NodeType != ExpressionType.Equal) + throw new NotSupportedException("JoinTyped currently supports only equality join expressions."); + + string leftPrefix = builder.Tables.FirstOrDefault().NullOr(t => string.IsNullOrEmpty(t.Alias) ? t.Name : t.Alias, null); + if (string.IsNullOrEmpty(leftPrefix)) + throw new InvalidOperationException("JoinTyped requires source table to be present."); + + string leftExpr = ResolveMappedSide(be.Left, typeof(TLeft), leftPrefix, builder.Database); + string rightExpr = ResolveMappedSide(be.Right, typeof(TRight), rightAlias, builder.Database); + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : builder.Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + builder.Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", joinType, rightTableExpr, rightAlias, leftExpr, rightExpr); + + builder.Join(x => joinExpr); + return builder; + } + + private static string ResolveMappedSide(Expression expression, Type modelType, string prefix, DynamicDatabase db) + { + expression = UnwrapConvert(expression); + var member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException("Join side must be mapped member access."); + + var mapper = DynamicMapperCache.GetMapper(modelType); + string col = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", prefix, db.DecorateName(col)); + } + + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + } +} diff --git a/DynamORM/Builders/TypedModifyExtensions.cs b/DynamORM/Builders/TypedModifyExtensions.cs new file mode 100644 index 0000000..f1687c9 --- /dev/null +++ b/DynamORM/Builders/TypedModifyExtensions.cs @@ -0,0 +1,155 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq; +using System.Linq.Expressions; +using DynamORM.Mapper; + +namespace DynamORM.Builders +{ + /// Typed helper extensions for update/insert/delete fluent APIs. + public static class TypedModifyExtensions + { + /// Add typed where predicate (AND-composed comparisons) for update builder. + public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) + { + AddTypedWhere(builder, predicate == null ? null : predicate.Body); + return builder; + } + + /// Add typed where predicate (AND-composed comparisons) for delete builder. + public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) + { + AddTypedWhere(builder, predicate == null ? null : predicate.Body); + return builder; + } + + /// Add typed value assignment for update builder. + public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) + { + string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); + return builder.Values(col, value); + } + + /// Add typed value assignment for insert builder. + public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) + { + string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); + return builder.Insert(col, value); + } + + /// Insert mapped object with compile-time type information. + public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class + { + return builder.Insert(value); + } + + /// Update mapped object with compile-time type information. + public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class + { + return builder.Update(value); + } + + private static void AddTypedWhere(dynamic builder, Expression expression) + { + if (expression == null) + throw new ArgumentNullException("predicate"); + + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + + if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) + { + AddTypedWhere(builder, be.Left); + AddTypedWhere(builder, be.Right); + return; + } + + if (be != null) + { + string col = GetColumnName(GetRootParameterType(be.Left), be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + builder.Where(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + builder.Where(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + builder.Where(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + builder.Where(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + builder.Where(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + builder.Where(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + } + + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + + private static Type GetRootParameterType(Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression m = expression as MemberExpression; + if (m != null && m.Expression is ParameterExpression) + return ((ParameterExpression)m.Expression).Type; + + throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); + } + + private static string GetColumnName(Type modelType, Expression expression) + { + if (modelType == null) + modelType = GetRootParameterType(expression); + + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + string col = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return col; + } + + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } +} From 33cadaf05ba01b2292495be039641ae9f33af07a Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 18:35:49 +0100 Subject: [PATCH 03/20] Add typed insert/update/delete builders and typed join variants --- AmalgamationTool/DynamORM.Amalgamation.cs | 449 ++++++++++++++---- .../Modify/TypedModifyExtensionsTests.cs | 40 ++ .../Select/TypedFluentBuilderTests.cs | 35 +- DynamORM/Builders/DynamicJoinType.cs | 17 + .../IDynamicTypedDeleteQueryBuilder.cs | 21 + .../IDynamicTypedInsertQueryBuilder.cs | 28 ++ .../IDynamicTypedSelectQueryBuilder.cs | 20 + .../IDynamicTypedUpdateQueryBuilder.cs | 33 ++ .../DynamicTypedDeleteQueryBuilder.cs | 57 +++ .../DynamicTypedInsertQueryBuilder.cs | 51 ++ .../DynamicTypedSelectQueryBuilder.cs | 102 ++++ .../DynamicTypedUpdateQueryBuilder.cs | 99 ++++ DynamORM/Builders/TypedJoinExtensions.cs | 94 ++-- DynamORM/DynamicDatabase.cs | 30 +- 14 files changed, 930 insertions(+), 146 deletions(-) create mode 100644 DynamORM/Builders/DynamicJoinType.cs create mode 100644 DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs create mode 100644 DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs create mode 100644 DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs create mode 100644 DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs create mode 100644 DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs create mode 100644 DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 528d5f2..767f058 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -2,31 +2,6 @@ * DynamORM - Dynamic Object-Relational Mapping library. * Copyright (c) 2012-2026, 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 DynamORM.Builders.Extensions; @@ -2003,9 +1978,11 @@ namespace DynamORM /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicInsertQueryBuilder Insert() + public virtual IDynamicTypedInsertQueryBuilder Insert() { - return new DynamicInsertQueryBuilder(this).Table(typeof(T)); + DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); + builder.Table(typeof(T)); + return builder; } /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. @@ -2103,9 +2080,11 @@ namespace DynamORM /// Adds to the UPDATE clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicUpdateQueryBuilder Update() + public virtual IDynamicTypedUpdateQueryBuilder Update() { - return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); + DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); + builder.Table(typeof(T)); + return builder; } /// Adds to the UPDATE clause using . /// Type which can be represented in database. @@ -2312,9 +2291,11 @@ namespace DynamORM /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicDeleteQueryBuilder Delete() + public virtual IDynamicTypedDeleteQueryBuilder Delete() { - return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); + DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); + builder.Table(typeof(T)); + return builder; } /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. @@ -6797,6 +6778,14 @@ namespace DynamORM } namespace Builders { + /// Typed join kind used by typed fluent builder APIs. + public enum DynamicJoinType + { + Inner = 0, + Left, + Right, + Full + } /// Dynamic delete query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder @@ -7164,10 +7153,55 @@ namespace DynamORM #endregion Top/Limit/Offset/Distinct } + /// Typed delete query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedDeleteQueryBuilder : IDynamicDeleteQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedDeleteQueryBuilder Where(Expression> predicate); + } + /// Typed insert query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedInsertQueryBuilder : IDynamicInsertQueryBuilder + { + /// Add typed insert assignment using mapped property selector. + /// Property type. + /// Property selector. + /// Value to insert. + /// Builder instance. + IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value); + + /// Add values from mapped object. + /// Mapped object value. + /// Builder instance. + IDynamicTypedInsertQueryBuilder Insert(T value); + } /// Typed select query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedSelectQueryBuilder : IDynamicSelectQueryBuilder { + /// Add typed join to mapped table. + /// Joined mapped entity type. + /// Join ON predicate. + /// Optional alias for joined table. + /// Join type. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + + /// Add INNER JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); + + /// Add LEFT JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null); + + /// Add RIGHT JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null); + + /// Add FULL JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null); + /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. @@ -7199,6 +7233,27 @@ namespace DynamORM /// Builder instance. IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); } + /// Typed update query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Where(Expression> predicate); + + /// Add typed assignment using mapped property selector. + /// Property type. + /// Property selector. + /// Assigned value. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value); + + /// Add update values from mapped object. + /// Mapped object value. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Values(T value); + } /// Dynamic update query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder @@ -7371,10 +7426,12 @@ namespace DynamORM return source; } } - /// Typed join helpers for typed select builder. + /// Compatibility and convenience extensions for typed joins. public static class TypedJoinExtensions { - /// Add typed join on mapped members. Supports simple equality join expression only. + /// + /// Legacy compatibility helper. Prefer . + /// public static IDynamicTypedSelectQueryBuilder JoinTyped( this IDynamicTypedSelectQueryBuilder builder, string alias, @@ -7384,60 +7441,49 @@ namespace DynamORM if (builder == null) throw new ArgumentNullException("builder"); if (on == null) throw new ArgumentNullException("on"); - var rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); - if (rightMapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + DynamicJoinType jt = DynamicJoinType.Inner; + string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant(); - string rightTable = string.IsNullOrEmpty(rightMapper.Table.NullOr(t => t.Name)) - ? typeof(TRight).Name - : rightMapper.Table.Name; + if (normalized == "LEFT JOIN" || normalized == "LEFT") + jt = DynamicJoinType.Left; + else if (normalized == "RIGHT JOIN" || normalized == "RIGHT") + jt = DynamicJoinType.Right; + else if (normalized == "FULL JOIN" || normalized == "FULL") + jt = DynamicJoinType.Full; - string rightOwner = rightMapper.Table.NullOr(t => t.Owner); - string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (builder.Tables.Count + 1).ToString() : alias; - - var be = on.Body as BinaryExpression; - if (be == null || be.NodeType != ExpressionType.Equal) - throw new NotSupportedException("JoinTyped currently supports only equality join expressions."); - - string leftPrefix = builder.Tables.FirstOrDefault().NullOr(t => string.IsNullOrEmpty(t.Alias) ? t.Name : t.Alias, null); - if (string.IsNullOrEmpty(leftPrefix)) - throw new InvalidOperationException("JoinTyped requires source table to be present."); - - string leftExpr = ResolveMappedSide(be.Left, typeof(TLeft), leftPrefix, builder.Database); - string rightExpr = ResolveMappedSide(be.Right, typeof(TRight), rightAlias, builder.Database); - - string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : builder.Database.DecorateName(rightOwner) + "."; - string rightTableExpr = ownerPrefix + builder.Database.DecorateName(rightTable); - string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", joinType, rightTableExpr, rightAlias, leftExpr, rightExpr); - - builder.Join(x => joinExpr); - return builder; + return builder.Join(on, alias, jt); } - private static string ResolveMappedSide(Expression expression, Type modelType, string prefix, DynamicDatabase db) + /// Convenience typed INNER JOIN extension. + public static IDynamicTypedSelectQueryBuilder InnerJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) { - expression = UnwrapConvert(expression); - var member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException("Join side must be mapped member access."); - - var mapper = DynamicMapperCache.GetMapper(modelType); - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return string.Format("{0}.{1}", prefix, db.DecorateName(col)); + return builder.Join(on, alias, DynamicJoinType.Inner); } - private static Expression UnwrapConvert(Expression expression) + /// Convenience typed LEFT JOIN extension. + public static IDynamicTypedSelectQueryBuilder LeftJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; - - return expression; + return builder.Join(on, alias, DynamicJoinType.Left); + } + /// Convenience typed RIGHT JOIN extension. + public static IDynamicTypedSelectQueryBuilder RightJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) + { + return builder.Join(on, alias, DynamicJoinType.Right); + } + /// Convenience typed FULL JOIN extension. + public static IDynamicTypedSelectQueryBuilder FullJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) + { + return builder.Join(on, alias, DynamicJoinType.Full); } } /// Typed helper extensions for update/insert/delete fluent APIs. @@ -10583,6 +10629,79 @@ namespace DynamORM } #endregion IExtendedDisposable } + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedDeleteQueryBuilder : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder + { + internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db) + : base(db) + { + } + public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) + { + this.WhereTyped(predicate); + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(Func func) + { + base.Where(func); + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + } + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedInsertQueryBuilder : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder + { + internal DynamicTypedInsertQueryBuilder(DynamicDatabase db) + : base(db) + { + } + public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) + { + this.InsertTyped(selector, value); + return this; + } + public IDynamicTypedInsertQueryBuilder Insert(T value) + { + base.Insert(value); + return this; + } + public new IDynamicTypedInsertQueryBuilder Values(Func fn, params Func[] func) + { + base.Values(fn, func); + return this; + } + public new IDynamicTypedInsertQueryBuilder Insert(string column, object value) + { + base.Insert(column, value); + return this; + } + public new IDynamicTypedInsertQueryBuilder Insert(object o) + { + base.Insert(o); + return this; + } + } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder @@ -10609,6 +10728,60 @@ namespace DynamORM return this; } + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner) + { + if (on == null) + throw new ArgumentNullException("on"); + + DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) + ? typeof(TRight).Name + : rightMapper.Table.Name; + string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; + string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (Tables.Count + 1).ToString() : alias; + + BinaryExpression be = on.Body as BinaryExpression; + if (be == null || be.NodeType != ExpressionType.Equal) + throw new NotSupportedException("Typed join expression is currently limited to equality comparisons."); + + string leftPrefix = GetRootAliasOrTableName(); + if (string.IsNullOrEmpty(leftPrefix)) + throw new InvalidOperationException("Join requires source table to be present."); + + string leftExpr = ParseTypedJoinMember(be.Left, leftPrefix, _mapper); + string rightExpr = ParseTypedJoinMember(be.Right, rightAlias, rightMapper); + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", + GetJoinKeyword(joinType), + rightTableExpr, + rightAlias, + leftExpr, + rightExpr); + + base.Join(x => joinExpr); + return this; + } + public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Inner); + } + public IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Left); + } + public IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Right); + } + public IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Full); + } public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) { base.Join(func); @@ -10960,6 +11133,46 @@ namespace DynamORM var parameter = member.Expression as ParameterExpression; return parameter != null && parameter.Type == typeof(T); } + private static string GetJoinKeyword(DynamicJoinType joinType) + { + switch (joinType) + { + case DynamicJoinType.Left: + return "LEFT JOIN"; + case DynamicJoinType.Right: + return "RIGHT JOIN"; + case DynamicJoinType.Full: + return "FULL JOIN"; + default: + return "INNER JOIN"; + } + } + private string ParseTypedJoinMember(Expression expression, string tablePrefix, DynamicTypeMap mapper) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null || !(member.Expression is ParameterExpression) || ((ParameterExpression)member.Expression).Type != typeof(TModel)) + throw new NotSupportedException(string.Format("Typed join member access is not supported: {0}", expression)); + + string mappedColumn = null; + PropertyInfo property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + if (string.IsNullOrEmpty(mappedColumn)) + mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); + } private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression unary && @@ -11014,6 +11227,80 @@ namespace DynamORM return getter(); } } + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder + { + internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db) + : base(db) + { + } + public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) + { + this.WhereTyped(predicate); + return this; + } + public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) + { + this.SetTyped(selector, value); + return this; + } + public IDynamicTypedUpdateQueryBuilder Values(T value) + { + base.Values(value); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Update(string column, object value) + { + base.Update(column, value); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Update(object conditions) + { + base.Update(conditions); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Set(params Func[] func) + { + base.Set(func); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Values(string column, object value) + { + base.Values(column, value); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Values(object o) + { + base.Values(o); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Where(Func func) + { + base.Where(func); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + } /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { diff --git a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs index a5390fa..0fb768e 100644 --- a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs +++ b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs @@ -47,6 +47,21 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } + [Test] + public void TestTypedUpdateBuilderSetAndWhere() + { + var cmd = Database.Update() + .Set(u => u.Code, "778") + .Where(u => u.Id == 1 && u.Code == "1"); + + Assert.AreEqual( + string.Format("UPDATE \"sample_users\" SET \"code\" = [${0}] WHERE (\"id\" = [${1}]) AND (\"code\" = [${2}])", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1), + cmd.Parameters.Keys.ElementAt(2)), + cmd.CommandText()); + } + [Test] public void TestTypedDeleteWhere() { @@ -58,6 +73,17 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } + [Test] + public void TestTypedDeleteBuilderWhere() + { + var cmd = Database.Delete() + .Where(u => u.Id == 3); + + Assert.AreEqual( + string.Format("DELETE FROM \"sample_users\" WHERE (\"id\" = [${0}])", cmd.Parameters.Keys.First()), + cmd.CommandText()); + } + [Test] public void TestTypedInsertColumns() { @@ -71,5 +97,19 @@ namespace DynamORM.Tests.Modify cmd.Parameters.Keys.ElementAt(1)), cmd.CommandText()); } + + [Test] + public void TestTypedInsertBuilderColumns() + { + var cmd = Database.Insert() + .Insert(u => u.Code, "901") + .Insert(u => u.First, "TypedB"); + + Assert.AreEqual( + string.Format("INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ([${0}], [${1}])", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1)), + cmd.CommandText()); + } } } diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index 6528378..ded6e1f 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -100,11 +100,44 @@ namespace DynamORM.Tests.Select public void TestTypedJoin() { var cmd = Database.From("u") - .JoinTyped("x", (l, r) => l.Id == r.Id) + .Join((l, r) => l.Id == r.Id, "x") .SelectTyped(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", cmd.CommandText()); } + + [Test] + public void TestTypedLeftJoin() + { + var cmd = Database.From("u") + .LeftJoin((l, r) => l.Id == r.Id, "x") + .SelectTyped(u => u.Id); + + Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", + cmd.CommandText()); + } + + [Test] + public void TestTypedRightJoin() + { + var cmd = Database.From("u") + .RightJoin((l, r) => l.Id == r.Id, "x") + .SelectTyped(u => u.Id); + + Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u RIGHT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", + cmd.CommandText()); + } + + [Test] + public void TestTypedFullJoin() + { + var cmd = Database.From("u") + .FullJoin((l, r) => l.Id == r.Id, "x") + .SelectTyped(u => u.Id); + + Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u FULL JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", + cmd.CommandText()); + } } } diff --git a/DynamORM/Builders/DynamicJoinType.cs b/DynamORM/Builders/DynamicJoinType.cs new file mode 100644 index 0000000..a4da19f --- /dev/null +++ b/DynamORM/Builders/DynamicJoinType.cs @@ -0,0 +1,17 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +namespace DynamORM.Builders +{ + /// Typed join kind used by typed fluent builder APIs. + public enum DynamicJoinType + { + Inner = 0, + Left, + Right, + Full + } +} diff --git a/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs new file mode 100644 index 0000000..3ce0b86 --- /dev/null +++ b/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs @@ -0,0 +1,21 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders +{ + /// Typed delete query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedDeleteQueryBuilder : IDynamicDeleteQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedDeleteQueryBuilder Where(Expression> predicate); + } +} diff --git a/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs new file mode 100644 index 0000000..2a1f8d4 --- /dev/null +++ b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs @@ -0,0 +1,28 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders +{ + /// Typed insert query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedInsertQueryBuilder : IDynamicInsertQueryBuilder + { + /// Add typed insert assignment using mapped property selector. + /// Property type. + /// Property selector. + /// Value to insert. + /// Builder instance. + IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value); + + /// Add values from mapped object. + /// Mapped object value. + /// Builder instance. + IDynamicTypedInsertQueryBuilder Insert(T value); + } +} diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index 2a7be96..0672ebe 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -35,6 +35,26 @@ namespace DynamORM.Builders /// Mapped entity type. public interface IDynamicTypedSelectQueryBuilder : IDynamicSelectQueryBuilder { + /// Add typed join to mapped table. + /// Joined mapped entity type. + /// Join ON predicate. + /// Optional alias for joined table. + /// Join type. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + + /// Add INNER JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); + + /// Add LEFT JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null); + + /// Add RIGHT JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null); + + /// Add FULL JOIN using typed ON predicate. + IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null); + /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. diff --git a/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs new file mode 100644 index 0000000..6285f12 --- /dev/null +++ b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs @@ -0,0 +1,33 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders +{ + /// Typed update query builder for mapped entities. + /// Mapped entity type. + public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder + { + /// Add typed where predicate using mapped properties. + /// Predicate to parse. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Where(Expression> predicate); + + /// Add typed assignment using mapped property selector. + /// Property type. + /// Property selector. + /// Assigned value. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value); + + /// Add update values from mapped object. + /// Mapped object value. + /// Builder instance. + IDynamicTypedUpdateQueryBuilder Values(T value); + } +} diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs new file mode 100644 index 0000000..65412de --- /dev/null +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -0,0 +1,57 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders.Implementation +{ + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedDeleteQueryBuilder : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder + { + internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db) + : base(db) + { + } + + public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) + { + this.WhereTyped(predicate); + return this; + } + + public new IDynamicTypedDeleteQueryBuilder Where(Func func) + { + base.Where(func); + return this; + } + + public new IDynamicTypedDeleteQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + + public new IDynamicTypedDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + + public new IDynamicTypedDeleteQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + + public new IDynamicTypedDeleteQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + } +} diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs new file mode 100644 index 0000000..8b072bf --- /dev/null +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -0,0 +1,51 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders.Implementation +{ + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedInsertQueryBuilder : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder + { + internal DynamicTypedInsertQueryBuilder(DynamicDatabase db) + : base(db) + { + } + + public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) + { + this.InsertTyped(selector, value); + return this; + } + + public IDynamicTypedInsertQueryBuilder Insert(T value) + { + base.Insert(value); + return this; + } + + public new IDynamicTypedInsertQueryBuilder Values(Func fn, params Func[] func) + { + base.Values(fn, func); + return this; + } + + public new IDynamicTypedInsertQueryBuilder Insert(string column, object value) + { + base.Insert(column, value); + return this; + } + + public new IDynamicTypedInsertQueryBuilder Insert(object o) + { + base.Insert(o); + return this; + } + } +} diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index b615a93..35cca6e 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -65,6 +65,65 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner) + { + if (on == null) + throw new ArgumentNullException("on"); + + DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) + ? typeof(TRight).Name + : rightMapper.Table.Name; + string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; + string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (Tables.Count + 1).ToString() : alias; + + BinaryExpression be = on.Body as BinaryExpression; + if (be == null || be.NodeType != ExpressionType.Equal) + throw new NotSupportedException("Typed join expression is currently limited to equality comparisons."); + + string leftPrefix = GetRootAliasOrTableName(); + if (string.IsNullOrEmpty(leftPrefix)) + throw new InvalidOperationException("Join requires source table to be present."); + + string leftExpr = ParseTypedJoinMember(be.Left, leftPrefix, _mapper); + string rightExpr = ParseTypedJoinMember(be.Right, rightAlias, rightMapper); + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", + GetJoinKeyword(joinType), + rightTableExpr, + rightAlias, + leftExpr, + rightExpr); + + base.Join(x => joinExpr); + return this; + } + + public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Inner); + } + + public IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Left); + } + + public IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Right); + } + + public IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null) + { + return Join(on, alias, DynamicJoinType.Full); + } + public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) { base.Join(func); @@ -454,6 +513,49 @@ namespace DynamORM.Builders.Implementation return parameter != null && parameter.Type == typeof(T); } + private static string GetJoinKeyword(DynamicJoinType joinType) + { + switch (joinType) + { + case DynamicJoinType.Left: + return "LEFT JOIN"; + case DynamicJoinType.Right: + return "RIGHT JOIN"; + case DynamicJoinType.Full: + return "FULL JOIN"; + default: + return "INNER JOIN"; + } + } + + private string ParseTypedJoinMember(Expression expression, string tablePrefix, DynamicTypeMap mapper) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null || !(member.Expression is ParameterExpression) || ((ParameterExpression)member.Expression).Type != typeof(TModel)) + throw new NotSupportedException(string.Format("Typed join member access is not supported: {0}", expression)); + + string mappedColumn = null; + PropertyInfo property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + + if (string.IsNullOrEmpty(mappedColumn)) + mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); + } + private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression unary && diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs new file mode 100644 index 0000000..a2227f6 --- /dev/null +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -0,0 +1,99 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders.Implementation +{ + /// Typed wrapper over with property-to-column translation. + /// Mapped entity type. + internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder + { + internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db) + : base(db) + { + } + + public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) + { + this.WhereTyped(predicate); + return this; + } + + public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) + { + this.SetTyped(selector, value); + return this; + } + + public IDynamicTypedUpdateQueryBuilder Values(T value) + { + base.Values(value); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Update(string column, object value) + { + base.Update(column, value); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Update(object conditions) + { + base.Update(conditions); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Set(params Func[] func) + { + base.Set(func); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Values(string column, object value) + { + base.Values(column, value); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Values(object o) + { + base.Values(o); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Where(Func func) + { + base.Where(func); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Where(DynamicColumn column) + { + base.Where(column); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + base.Where(column, op, value); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Where(string column, object value) + { + base.Where(column, value); + return this; + } + + public new IDynamicTypedUpdateQueryBuilder Where(object conditions, bool schema = false) + { + base.Where(conditions, schema); + return this; + } + } +} diff --git a/DynamORM/Builders/TypedJoinExtensions.cs b/DynamORM/Builders/TypedJoinExtensions.cs index 6ce4e27..c3b5d07 100644 --- a/DynamORM/Builders/TypedJoinExtensions.cs +++ b/DynamORM/Builders/TypedJoinExtensions.cs @@ -5,17 +5,16 @@ */ using System; -using System.Linq; using System.Linq.Expressions; -using DynamORM.Helpers; -using DynamORM.Mapper; namespace DynamORM.Builders { - /// Typed join helpers for typed select builder. + /// Compatibility and convenience extensions for typed joins. public static class TypedJoinExtensions { - /// Add typed join on mapped members. Supports simple equality join expression only. + /// + /// Legacy compatibility helper. Prefer . + /// public static IDynamicTypedSelectQueryBuilder JoinTyped( this IDynamicTypedSelectQueryBuilder builder, string alias, @@ -25,62 +24,53 @@ namespace DynamORM.Builders if (builder == null) throw new ArgumentNullException("builder"); if (on == null) throw new ArgumentNullException("on"); - var rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); - if (rightMapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + DynamicJoinType jt = DynamicJoinType.Inner; + string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant(); - string rightTable = string.IsNullOrEmpty(rightMapper.Table.NullOr(t => t.Name)) - ? typeof(TRight).Name - : rightMapper.Table.Name; + if (normalized == "LEFT JOIN" || normalized == "LEFT") + jt = DynamicJoinType.Left; + else if (normalized == "RIGHT JOIN" || normalized == "RIGHT") + jt = DynamicJoinType.Right; + else if (normalized == "FULL JOIN" || normalized == "FULL") + jt = DynamicJoinType.Full; - string rightOwner = rightMapper.Table.NullOr(t => t.Owner); - string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (builder.Tables.Count + 1).ToString() : alias; - - var be = on.Body as BinaryExpression; - if (be == null || be.NodeType != ExpressionType.Equal) - throw new NotSupportedException("JoinTyped currently supports only equality join expressions."); - - string leftPrefix = builder.Tables.FirstOrDefault().NullOr(t => string.IsNullOrEmpty(t.Alias) ? t.Name : t.Alias, null); - if (string.IsNullOrEmpty(leftPrefix)) - throw new InvalidOperationException("JoinTyped requires source table to be present."); - - string leftExpr = ResolveMappedSide(be.Left, typeof(TLeft), leftPrefix, builder.Database); - string rightExpr = ResolveMappedSide(be.Right, typeof(TRight), rightAlias, builder.Database); - - string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : builder.Database.DecorateName(rightOwner) + "."; - string rightTableExpr = ownerPrefix + builder.Database.DecorateName(rightTable); - string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", joinType, rightTableExpr, rightAlias, leftExpr, rightExpr); - - builder.Join(x => joinExpr); - return builder; + return builder.Join(on, alias, jt); } - private static string ResolveMappedSide(Expression expression, Type modelType, string prefix, DynamicDatabase db) + /// Convenience typed INNER JOIN extension. + public static IDynamicTypedSelectQueryBuilder InnerJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) { - expression = UnwrapConvert(expression); - var member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException("Join side must be mapped member access."); - - var mapper = DynamicMapperCache.GetMapper(modelType); - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return string.Format("{0}.{1}", prefix, db.DecorateName(col)); + return builder.Join(on, alias, DynamicJoinType.Inner); } - private static Expression UnwrapConvert(Expression expression) + /// Convenience typed LEFT JOIN extension. + public static IDynamicTypedSelectQueryBuilder LeftJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; + return builder.Join(on, alias, DynamicJoinType.Left); + } - return expression; + /// Convenience typed RIGHT JOIN extension. + public static IDynamicTypedSelectQueryBuilder RightJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) + { + return builder.Join(on, alias, DynamicJoinType.Right); + } + + /// Convenience typed FULL JOIN extension. + public static IDynamicTypedSelectQueryBuilder FullJoinTyped( + this IDynamicTypedSelectQueryBuilder builder, + Expression> on, + string alias = null) + { + return builder.Join(on, alias, DynamicJoinType.Full); } } } diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index 39695e1..a329123 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -503,10 +503,12 @@ namespace DynamORM /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicInsertQueryBuilder Insert() - { - return new DynamicInsertQueryBuilder(this).Table(typeof(T)); - } + public virtual IDynamicTypedInsertQueryBuilder Insert() + { + DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. @@ -610,10 +612,12 @@ namespace DynamORM /// Adds to the UPDATE clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicUpdateQueryBuilder Update() - { - return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); - } + public virtual IDynamicTypedUpdateQueryBuilder Update() + { + DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the UPDATE clause using . /// Type which can be represented in database. @@ -831,10 +835,12 @@ namespace DynamORM /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicDeleteQueryBuilder Delete() - { - return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); - } + public virtual IDynamicTypedDeleteQueryBuilder Delete() + { + DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. From f28549c7755a1c2b1895dff0c7edd8f2860b66b9 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 18:40:43 +0100 Subject: [PATCH 04/20] Remove redundant typed fluent wrapper methods --- AmalgamationTool/DynamORM.Amalgamation.cs | 24 ----------------- .../Select/TypedFluentBuilderTests.cs | 20 +++++++------- DynamORM/Builders/TypedFluentExtensions.cs | 27 ------------------- 3 files changed, 10 insertions(+), 61 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 767f058..049ade1 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7386,30 +7386,6 @@ namespace DynamORM /// Marker extensions for typed fluent builder expressions. public static class TypedFluentExtensions { - /// Typed select helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder SelectTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.Select(selector, selectors); - } - /// Typed group by helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder GroupByTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.GroupBy(selector, selectors); - } - /// Typed order by helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder OrderByTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.OrderBy(selector, selectors); - } /// Marks select projection alias in typed expressions. public static T As(this T source, string alias) { diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index ded6e1f..acbe033 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -58,8 +58,8 @@ namespace DynamORM.Tests.Select { var cmd = Database.From("u") .Where(u => u.Id == 1) - .SelectTyped(u => u.Code.As("CodeAlias")) - .OrderByTyped(u => u.Code.Desc()); + .Select(u => u.Code.As("CodeAlias")) + .OrderBy(u => u.Code.Desc()); Assert.AreEqual( string.Format("SELECT u.\"user_code\" AS \"CodeAlias\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" = [${0}]) ORDER BY u.\"user_code\" DESC", cmd.Parameters.Keys.First()), @@ -73,7 +73,7 @@ namespace DynamORM.Tests.Select var cmd = Database.From("u") .Where(u => ids.Contains(u.Id)) - .SelectTyped(u => u.Id); + .Select(u => u.Id); Assert.AreEqual( string.Format("SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (u.\"id_user\" IN([${0}], [${1}], [${2}]))", @@ -87,10 +87,10 @@ namespace DynamORM.Tests.Select public void TestTypedGroupByHavingOrderBy() { var cmd = Database.From("u") - .SelectTyped(u => u.Code) - .GroupByTyped(u => u.Code) + .Select(u => u.Code) + .GroupBy(u => u.Code) .Having(u => u.Code != null) - .OrderByTyped(u => u.Code.Asc()); + .OrderBy(u => u.Code.Asc()); Assert.AreEqual("SELECT u.\"user_code\" FROM \"sample_users\" AS u GROUP BY u.\"user_code\" HAVING (u.\"user_code\" IS NOT NULL) ORDER BY u.\"user_code\" ASC", cmd.CommandText()); @@ -101,7 +101,7 @@ namespace DynamORM.Tests.Select { var cmd = Database.From("u") .Join((l, r) => l.Id == r.Id, "x") - .SelectTyped(u => u.Id); + .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", cmd.CommandText()); @@ -112,7 +112,7 @@ namespace DynamORM.Tests.Select { var cmd = Database.From("u") .LeftJoin((l, r) => l.Id == r.Id, "x") - .SelectTyped(u => u.Id); + .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", cmd.CommandText()); @@ -123,7 +123,7 @@ namespace DynamORM.Tests.Select { var cmd = Database.From("u") .RightJoin((l, r) => l.Id == r.Id, "x") - .SelectTyped(u => u.Id); + .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u RIGHT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", cmd.CommandText()); @@ -134,7 +134,7 @@ namespace DynamORM.Tests.Select { var cmd = Database.From("u") .FullJoin((l, r) => l.Id == r.Id, "x") - .SelectTyped(u => u.Id); + .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u FULL JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", cmd.CommandText()); diff --git a/DynamORM/Builders/TypedFluentExtensions.cs b/DynamORM/Builders/TypedFluentExtensions.cs index a79f331..b29d4bb 100644 --- a/DynamORM/Builders/TypedFluentExtensions.cs +++ b/DynamORM/Builders/TypedFluentExtensions.cs @@ -31,33 +31,6 @@ namespace DynamORM.Builders /// Marker extensions for typed fluent builder expressions. public static class TypedFluentExtensions { - /// Typed select helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder SelectTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.Select(selector, selectors); - } - - /// Typed group by helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder GroupByTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.GroupBy(selector, selectors); - } - - /// Typed order by helper that avoids overload resolution with dynamic methods. - public static IDynamicTypedSelectQueryBuilder OrderByTyped( - this IDynamicTypedSelectQueryBuilder builder, - System.Linq.Expressions.Expression> selector, - params System.Linq.Expressions.Expression>[] selectors) - { - return builder.OrderBy(selector, selectors); - } - /// Marks select projection alias in typed expressions. public static T As(this T source, string alias) { From 23a0336b7ee87543aeb043b1a775fff0a0d4f7ed Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 18:42:35 +0100 Subject: [PATCH 05/20] Add typed join specification builder syntax --- AmalgamationTool/DynamORM.Amalgamation.cs | 77 +++++++++++++++++++ .../Select/TypedFluentBuilderTests.cs | 4 +- .../IDynamicTypedSelectQueryBuilder.cs | 6 ++ .../DynamicTypedSelectQueryBuilder.cs | 14 ++++ DynamORM/Builders/TypedJoinBuilder.cs | 76 ++++++++++++++++++ 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 DynamORM/Builders/TypedJoinBuilder.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 049ade1..ccef0e0 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7190,6 +7190,12 @@ namespace DynamORM /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + /// Add typed join using join-spec builder syntax (As(), join kind and On()). + /// Joined mapped entity type. + /// Join specification builder callback. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification); + /// Add INNER JOIN using typed ON predicate. IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); @@ -7402,6 +7408,64 @@ namespace DynamORM return source; } } + /// Typed join specification builder used by typed fluent select queries. + /// Left side mapped type. + /// Right side mapped type. + public class TypedJoinBuilder + { + internal TypedJoinBuilder() + { + JoinType = DynamicJoinType.Inner; + } + /// Gets join alias. + public string Alias { get; private set; } + + /// Gets join type. + public DynamicJoinType JoinType { get; private set; } + + /// Gets ON predicate. + public Expression> OnPredicate { get; private set; } + + /// Sets join alias. + public TypedJoinBuilder As(string alias) + { + Alias = alias; + return this; + } + /// Sets INNER JOIN. + public TypedJoinBuilder Inner() + { + JoinType = DynamicJoinType.Inner; + return this; + } + /// Sets LEFT JOIN. + public TypedJoinBuilder Left() + { + JoinType = DynamicJoinType.Left; + return this; + } + /// Sets RIGHT JOIN. + public TypedJoinBuilder Right() + { + JoinType = DynamicJoinType.Right; + return this; + } + /// Sets FULL JOIN. + public TypedJoinBuilder Full() + { + JoinType = DynamicJoinType.Full; + return this; + } + /// Sets ON predicate. + public TypedJoinBuilder On(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnPredicate = predicate; + return this; + } + } /// Compatibility and convenience extensions for typed joins. public static class TypedJoinExtensions { @@ -10742,6 +10806,19 @@ namespace DynamORM base.Join(x => joinExpr); return this; } + public IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedJoinBuilder spec = specification(new TypedJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + if (spec.OnPredicate == null) + throw new ArgumentException("Join specification must define ON predicate.", "specification"); + + return Join(spec.OnPredicate, spec.Alias, spec.JoinType); + } public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) { return Join(on, alias, DynamicJoinType.Inner); diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index acbe033..c8c16b8 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -100,7 +100,7 @@ namespace DynamORM.Tests.Select public void TestTypedJoin() { var cmd = Database.From("u") - .Join((l, r) => l.Id == r.Id, "x") + .Join(j => j.As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", @@ -111,7 +111,7 @@ namespace DynamORM.Tests.Select public void TestTypedLeftJoin() { var cmd = Database.From("u") - .LeftJoin((l, r) => l.Id == r.Id, "x") + .Join(j => j.Left().As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index 0672ebe..bf4f420 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -43,6 +43,12 @@ namespace DynamORM.Builders /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + /// Add typed join using join-spec builder syntax (As(), join kind and On()). + /// Joined mapped entity type. + /// Join specification builder callback. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification); + /// Add INNER JOIN using typed ON predicate. IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index 35cca6e..1e8053a 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -104,6 +104,20 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedJoinBuilder spec = specification(new TypedJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + if (spec.OnPredicate == null) + throw new ArgumentException("Join specification must define ON predicate.", "specification"); + + return Join(spec.OnPredicate, spec.Alias, spec.JoinType); + } + public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) { return Join(on, alias, DynamicJoinType.Inner); diff --git a/DynamORM/Builders/TypedJoinBuilder.cs b/DynamORM/Builders/TypedJoinBuilder.cs new file mode 100644 index 0000000..ebc5bb3 --- /dev/null +++ b/DynamORM/Builders/TypedJoinBuilder.cs @@ -0,0 +1,76 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.Builders +{ + /// Typed join specification builder used by typed fluent select queries. + /// Left side mapped type. + /// Right side mapped type. + public class TypedJoinBuilder + { + internal TypedJoinBuilder() + { + JoinType = DynamicJoinType.Inner; + } + + /// Gets join alias. + public string Alias { get; private set; } + + /// Gets join type. + public DynamicJoinType JoinType { get; private set; } + + /// Gets ON predicate. + public Expression> OnPredicate { get; private set; } + + /// Sets join alias. + public TypedJoinBuilder As(string alias) + { + Alias = alias; + return this; + } + + /// Sets INNER JOIN. + public TypedJoinBuilder Inner() + { + JoinType = DynamicJoinType.Inner; + return this; + } + + /// Sets LEFT JOIN. + public TypedJoinBuilder Left() + { + JoinType = DynamicJoinType.Left; + return this; + } + + /// Sets RIGHT JOIN. + public TypedJoinBuilder Right() + { + JoinType = DynamicJoinType.Right; + return this; + } + + /// Sets FULL JOIN. + public TypedJoinBuilder Full() + { + JoinType = DynamicJoinType.Full; + return this; + } + + /// Sets ON predicate. + public TypedJoinBuilder On(Expression> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnPredicate = predicate; + return this; + } + } +} From 13936d8598bf6f93a7859c90bb97716b2fc781b2 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 18:48:01 +0100 Subject: [PATCH 06/20] Remove typed modify extension methods in favor of typed builders --- AmalgamationTool/DynamORM.Amalgamation.cs | 230 ++++++++---------- .../Modify/TypedModifyExtensionsTests.cs | 40 --- .../DynamicTypedDeleteQueryBuilder.cs | 2 +- .../DynamicTypedInsertQueryBuilder.cs | 2 +- .../DynamicTypedUpdateQueryBuilder.cs | 4 +- .../Implementation/TypedModifyHelper.cs | 114 +++++++++ DynamORM/Builders/TypedModifyExtensions.cs | 155 ------------ 7 files changed, 215 insertions(+), 332 deletions(-) create mode 100644 DynamORM/Builders/Implementation/TypedModifyHelper.cs delete mode 100644 DynamORM/Builders/TypedModifyExtensions.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index ccef0e0..4f01c18 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7526,135 +7526,6 @@ namespace DynamORM return builder.Join(on, alias, DynamicJoinType.Full); } } - /// Typed helper extensions for update/insert/delete fluent APIs. - public static class TypedModifyExtensions - { - /// Add typed where predicate (AND-composed comparisons) for update builder. - public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - /// Add typed where predicate (AND-composed comparisons) for delete builder. - public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - /// Add typed value assignment for update builder. - public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Values(col, value); - } - /// Add typed value assignment for insert builder. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Insert(col, value); - } - /// Insert mapped object with compile-time type information. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class - { - return builder.Insert(value); - } - /// Update mapped object with compile-time type information. - public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class - { - return builder.Update(value); - } - private static void AddTypedWhere(dynamic builder, Expression expression) - { - if (expression == null) - throw new ArgumentNullException("predicate"); - - expression = UnwrapConvert(expression); - BinaryExpression be = expression as BinaryExpression; - - if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) - { - AddTypedWhere(builder, be.Left); - AddTypedWhere(builder, be.Right); - return; - } - if (be != null) - { - string col = GetColumnName(GetRootParameterType(be.Left), be.Left); - object val = EvaluateExpression(be.Right); - - switch (be.NodeType) - { - case ExpressionType.Equal: - builder.Where(col, DynamicColumn.CompareOperator.Eq, val); - return; - case ExpressionType.NotEqual: - builder.Where(col, DynamicColumn.CompareOperator.Not, val); - return; - case ExpressionType.GreaterThan: - builder.Where(col, DynamicColumn.CompareOperator.Gt, val); - return; - case ExpressionType.GreaterThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Gte, val); - return; - case ExpressionType.LessThan: - builder.Where(col, DynamicColumn.CompareOperator.Lt, val); - return; - case ExpressionType.LessThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Lte, val); - return; - } - } - throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); - } - private static Type GetRootParameterType(Expression expression) - { - expression = UnwrapConvert(expression); - MemberExpression m = expression as MemberExpression; - if (m != null && m.Expression is ParameterExpression) - return ((ParameterExpression)m.Expression).Type; - - throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); - } - private static string GetColumnName(Type modelType, Expression expression) - { - if (modelType == null) - modelType = GetRootParameterType(expression); - - expression = UnwrapConvert(expression); - MemberExpression member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); - - DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); - if (mapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); - - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return col; - } - private static Expression UnwrapConvert(Expression expression) - { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; - - return expression; - } - private static object EvaluateExpression(Expression expression) - { - expression = UnwrapConvert(expression); - var objectMember = Expression.Convert(expression, typeof(object)); - var getter = Expression.Lambda>(objectMember).Compile(); - return getter(); - } - } namespace Extensions { internal static class DynamicHavingQueryExtensions @@ -10679,7 +10550,7 @@ namespace DynamORM } public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public new IDynamicTypedDeleteQueryBuilder Where(Func func) @@ -10718,7 +10589,7 @@ namespace DynamORM } public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { - this.InsertTyped(selector, value); + base.Insert(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedInsertQueryBuilder Insert(T value) @@ -11290,12 +11161,12 @@ namespace DynamORM } public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) { - this.SetTyped(selector, value); + base.Values(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedUpdateQueryBuilder Values(T value) @@ -11634,6 +11505,99 @@ namespace DynamORM } #endregion IExtendedDisposable } + /// Helper methods for typed modify builders. + internal static class TypedModifyHelper + { + public static string GetMappedColumn(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + return GetMappedColumn(typeof(T), selector.Body); + } + public static void ApplyWhere(Action addCondition, Expression> predicate) + { + if (addCondition == null) + throw new ArgumentNullException("addCondition"); + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ApplyWhereInternal(typeof(T), addCondition, predicate.Body); + } + private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) + { + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + if (be == null) + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + + if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And) + { + ApplyWhereInternal(modelType, addCondition, be.Left); + ApplyWhereInternal(modelType, addCondition, be.Right); + return; + } + string col = GetMappedColumn(modelType, be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + addCondition(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + addCondition(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + addCondition(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + addCondition(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + private static string GetMappedColumn(Type modelType, Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + } + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } } } namespace Helpers diff --git a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs index 0fb768e..ac41e42 100644 --- a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs +++ b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs @@ -32,21 +32,6 @@ namespace DynamORM.Tests.Modify DestroyTestDatabase(); } - [Test] - public void TestTypedUpdateSetAndWhere() - { - var cmd = Database.Update() - .SetTyped(u => u.Code, "777") - .WhereTyped(u => u.Id == 1 && u.Code == "1"); - - Assert.AreEqual( - string.Format("UPDATE \"sample_users\" SET \"code\" = [${0}] WHERE (\"id\" = [${1}]) AND (\"code\" = [${2}])", - cmd.Parameters.Keys.ElementAt(0), - cmd.Parameters.Keys.ElementAt(1), - cmd.Parameters.Keys.ElementAt(2)), - cmd.CommandText()); - } - [Test] public void TestTypedUpdateBuilderSetAndWhere() { @@ -62,17 +47,6 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } - [Test] - public void TestTypedDeleteWhere() - { - var cmd = Database.Delete() - .WhereTyped(u => u.Id == 2); - - Assert.AreEqual( - string.Format("DELETE FROM \"sample_users\" WHERE (\"id\" = [${0}])", cmd.Parameters.Keys.First()), - cmd.CommandText()); - } - [Test] public void TestTypedDeleteBuilderWhere() { @@ -84,20 +58,6 @@ namespace DynamORM.Tests.Modify cmd.CommandText()); } - [Test] - public void TestTypedInsertColumns() - { - var cmd = Database.Insert() - .InsertTyped(u => u.Code, "900") - .InsertTyped(u => u.First, "Typed"); - - Assert.AreEqual( - string.Format("INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES ([${0}], [${1}])", - cmd.Parameters.Keys.ElementAt(0), - cmd.Parameters.Keys.ElementAt(1)), - cmd.CommandText()); - } - [Test] public void TestTypedInsertBuilderColumns() { diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs index 65412de..eddbd13 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -20,7 +20,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs index 8b072bf..257f7ee 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -20,7 +20,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { - this.InsertTyped(selector, value); + base.Insert(TypedModifyHelper.GetMappedColumn(selector), value); return this; } diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs index a2227f6..efa2c26 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -20,13 +20,13 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { - this.WhereTyped(predicate); + TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) { - this.SetTyped(selector, value); + base.Values(TypedModifyHelper.GetMappedColumn(selector), value); return this; } diff --git a/DynamORM/Builders/Implementation/TypedModifyHelper.cs b/DynamORM/Builders/Implementation/TypedModifyHelper.cs new file mode 100644 index 0000000..8625026 --- /dev/null +++ b/DynamORM/Builders/Implementation/TypedModifyHelper.cs @@ -0,0 +1,114 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq; +using System.Linq.Expressions; +using DynamORM.Mapper; + +namespace DynamORM.Builders.Implementation +{ + /// Helper methods for typed modify builders. + internal static class TypedModifyHelper + { + public static string GetMappedColumn(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + return GetMappedColumn(typeof(T), selector.Body); + } + + public static void ApplyWhere(Action addCondition, Expression> predicate) + { + if (addCondition == null) + throw new ArgumentNullException("addCondition"); + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ApplyWhereInternal(typeof(T), addCondition, predicate.Body); + } + + private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) + { + expression = UnwrapConvert(expression); + BinaryExpression be = expression as BinaryExpression; + if (be == null) + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + + if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And) + { + ApplyWhereInternal(modelType, addCondition, be.Left); + ApplyWhereInternal(modelType, addCondition, be.Right); + return; + } + + string col = GetMappedColumn(modelType, be.Left); + object val = EvaluateExpression(be.Right); + + switch (be.NodeType) + { + case ExpressionType.Equal: + addCondition(col, DynamicColumn.CompareOperator.Eq, val); + return; + case ExpressionType.NotEqual: + addCondition(col, DynamicColumn.CompareOperator.Not, val); + return; + case ExpressionType.GreaterThan: + addCondition(col, DynamicColumn.CompareOperator.Gt, val); + return; + case ExpressionType.GreaterThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Gte, val); + return; + case ExpressionType.LessThan: + addCondition(col, DynamicColumn.CompareOperator.Lt, val); + return; + case ExpressionType.LessThanOrEqual: + addCondition(col, DynamicColumn.CompareOperator.Lte, val); + return; + } + + throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); + } + + private static string GetMappedColumn(Type modelType, Expression expression) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member == null) + throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); + + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + } + + private static Expression UnwrapConvert(Expression expression) + { + while (expression is UnaryExpression && + (((UnaryExpression)expression).NodeType == ExpressionType.Convert || + ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) + expression = ((UnaryExpression)expression).Operand; + + return expression; + } + + private static object EvaluateExpression(Expression expression) + { + expression = UnwrapConvert(expression); + var objectMember = Expression.Convert(expression, typeof(object)); + var getter = Expression.Lambda>(objectMember).Compile(); + return getter(); + } + } +} diff --git a/DynamORM/Builders/TypedModifyExtensions.cs b/DynamORM/Builders/TypedModifyExtensions.cs deleted file mode 100644 index f1687c9..0000000 --- a/DynamORM/Builders/TypedModifyExtensions.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* - * DynamORM - Dynamic Object-Relational Mapping library. - * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) - * All rights reserved. - */ - -using System; -using System.Linq; -using System.Linq.Expressions; -using DynamORM.Mapper; - -namespace DynamORM.Builders -{ - /// Typed helper extensions for update/insert/delete fluent APIs. - public static class TypedModifyExtensions - { - /// Add typed where predicate (AND-composed comparisons) for update builder. - public static IDynamicUpdateQueryBuilder WhereTyped(this IDynamicUpdateQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - - /// Add typed where predicate (AND-composed comparisons) for delete builder. - public static IDynamicDeleteQueryBuilder WhereTyped(this IDynamicDeleteQueryBuilder builder, Expression> predicate) - { - AddTypedWhere(builder, predicate == null ? null : predicate.Body); - return builder; - } - - /// Add typed value assignment for update builder. - public static IDynamicUpdateQueryBuilder SetTyped(this IDynamicUpdateQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Values(col, value); - } - - /// Add typed value assignment for insert builder. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, Expression> selector, object value) - { - string col = GetColumnName(typeof(T), selector == null ? null : selector.Body); - return builder.Insert(col, value); - } - - /// Insert mapped object with compile-time type information. - public static IDynamicInsertQueryBuilder InsertTyped(this IDynamicInsertQueryBuilder builder, T value) where T : class - { - return builder.Insert(value); - } - - /// Update mapped object with compile-time type information. - public static IDynamicUpdateQueryBuilder UpdateTyped(this IDynamicUpdateQueryBuilder builder, T value) where T : class - { - return builder.Update(value); - } - - private static void AddTypedWhere(dynamic builder, Expression expression) - { - if (expression == null) - throw new ArgumentNullException("predicate"); - - expression = UnwrapConvert(expression); - BinaryExpression be = expression as BinaryExpression; - - if (be != null && (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And)) - { - AddTypedWhere(builder, be.Left); - AddTypedWhere(builder, be.Right); - return; - } - - if (be != null) - { - string col = GetColumnName(GetRootParameterType(be.Left), be.Left); - object val = EvaluateExpression(be.Right); - - switch (be.NodeType) - { - case ExpressionType.Equal: - builder.Where(col, DynamicColumn.CompareOperator.Eq, val); - return; - case ExpressionType.NotEqual: - builder.Where(col, DynamicColumn.CompareOperator.Not, val); - return; - case ExpressionType.GreaterThan: - builder.Where(col, DynamicColumn.CompareOperator.Gt, val); - return; - case ExpressionType.GreaterThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Gte, val); - return; - case ExpressionType.LessThan: - builder.Where(col, DynamicColumn.CompareOperator.Lt, val); - return; - case ExpressionType.LessThanOrEqual: - builder.Where(col, DynamicColumn.CompareOperator.Lte, val); - return; - } - } - - throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); - } - - private static Type GetRootParameterType(Expression expression) - { - expression = UnwrapConvert(expression); - MemberExpression m = expression as MemberExpression; - if (m != null && m.Expression is ParameterExpression) - return ((ParameterExpression)m.Expression).Type; - - throw new NotSupportedException(string.Format("Unsupported typed selector: {0}", expression)); - } - - private static string GetColumnName(Type modelType, Expression expression) - { - if (modelType == null) - modelType = GetRootParameterType(expression); - - expression = UnwrapConvert(expression); - MemberExpression member = expression as MemberExpression; - if (member == null) - throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); - - DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); - if (mapper == null) - throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); - - string col = mapper.PropertyMap.TryGetValue(member.Member.Name) - ?? mapper.PropertyMap - .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .FirstOrDefault() - ?? member.Member.Name; - - return col; - } - - private static Expression UnwrapConvert(Expression expression) - { - while (expression is UnaryExpression && - (((UnaryExpression)expression).NodeType == ExpressionType.Convert || - ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) - expression = ((UnaryExpression)expression).Operand; - - return expression; - } - - private static object EvaluateExpression(Expression expression) - { - expression = UnwrapConvert(expression); - var objectMember = Expression.Convert(expression, typeof(object)); - var getter = Expression.Lambda>(objectMember).Compile(); - return getter(); - } - } -} From 15e1d9031c326d998f82fd021c08ad97fb96aeb7 Mon Sep 17 00:00:00 2001 From: Grzegorz Russek Date: Thu, 26 Feb 2026 19:38:28 +0100 Subject: [PATCH 07/20] Manual fix for more backward compatibility --- DynamORM.Tests/Modify/ParserTests.cs | 2 +- .../Modify/TypedModifyExtensionsTests.cs | 8 +- DynamORM.Tests/Modify/TypedParserTests.cs | 224 +++++ .../Select/TypedFluentBuilderTests.cs | 19 +- DynamORM.Tests/Select/TypedParserTests.cs | 860 ++++++++++++++++++ .../IDynamicTypedSelectQueryBuilder.cs | 12 - .../DynamicTypedSelectQueryBuilder.cs | 20 - DynamORM/DynamicDatabase.cs | 166 ++-- 8 files changed, 1204 insertions(+), 107 deletions(-) create mode 100644 DynamORM.Tests/Modify/TypedParserTests.cs create mode 100644 DynamORM.Tests/Select/TypedParserTests.cs diff --git a/DynamORM.Tests/Modify/ParserTests.cs b/DynamORM.Tests/Modify/ParserTests.cs index 9cdc1c6..a5216ab 100644 --- a/DynamORM.Tests/Modify/ParserTests.cs +++ b/DynamORM.Tests/Modify/ParserTests.cs @@ -66,7 +66,7 @@ namespace DynamORM.Tests.Modify /// Tests the basic insert. /// [Test] - public void TestInsertBasic() + public void TestTypedInsertBasic() { IDynamicInsertQueryBuilder cmd = new DynamicInsertQueryBuilder(Database, "Users"); diff --git a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs index ac41e42..d138246 100644 --- a/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs +++ b/DynamORM.Tests/Modify/TypedModifyExtensionsTests.cs @@ -4,8 +4,6 @@ * All rights reserved. */ -using System.Linq; -using DynamORM.Builders; using DynamORM.Tests.Helpers; using NUnit.Framework; @@ -35,7 +33,7 @@ namespace DynamORM.Tests.Modify [Test] public void TestTypedUpdateBuilderSetAndWhere() { - var cmd = Database.Update() + var cmd = Database.UpdateTyped() .Set(u => u.Code, "778") .Where(u => u.Id == 1 && u.Code == "1"); @@ -50,7 +48,7 @@ namespace DynamORM.Tests.Modify [Test] public void TestTypedDeleteBuilderWhere() { - var cmd = Database.Delete() + var cmd = Database.DeleteTyped() .Where(u => u.Id == 3); Assert.AreEqual( @@ -61,7 +59,7 @@ namespace DynamORM.Tests.Modify [Test] public void TestTypedInsertBuilderColumns() { - var cmd = Database.Insert() + var cmd = Database.InsertTyped() .Insert(u => u.Code, "901") .Insert(u => u.First, "TypedB"); diff --git a/DynamORM.Tests/Modify/TypedParserTests.cs b/DynamORM.Tests/Modify/TypedParserTests.cs new file mode 100644 index 0000000..e20ae79 --- /dev/null +++ b/DynamORM.Tests/Modify/TypedParserTests.cs @@ -0,0 +1,224 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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.Linq; +using DynamORM.Builders; +using DynamORM.Builders.Implementation; +using NUnit.Framework; +using DynamORM.Tests.Helpers; +using System.Collections.Generic; +using static System.Data.Entity.Infrastructure.Design.Executor; +using System.Runtime.InteropServices; + +namespace DynamORM.Tests.Modify +{ + /// New parser tests. + [TestFixture] + public class TypedParserTests : TestsBase + { + /// Setup test parameters. + [SetUp] + public virtual void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset); + } + + /// Tear down test objects. + [TearDown] + public virtual void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + #region Insert + + /// + /// Tests the basic insert. + /// + [Test] + public void TestTypedInsertBasic() + { + var cmd = new DynamicTypedInsertQueryBuilder(Database); + + cmd.Values(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = 1); + + Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0})", + string.Join(", ", cmd.Parameters.Keys.Select(p => string.Format("[${0}]", p)))), cmd.CommandText()); + } + + /// + /// Tests the insert with sub query. + /// + [Test] + public void TestInsertSubQuery() + { + var cmd = new DynamicTypedInsertQueryBuilder(Database); + + cmd.Values(x => x.Code = "001", x => x.Name = "Admin", x => x.IsAdmin = x(cmd + .SubQuery(a => a.AccessRights.As(a.a)) + .Select(a => a.IsAdmin) + .Where(a => a.User_Id == "001"))); + + Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0}, (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = [${1}])))", + string.Join(", ", cmd.Parameters.Keys.Take(2).Select(p => string.Format("[${0}]", p))), cmd.Parameters.Keys.Last()), cmd.CommandText()); + } + + /// + /// Tests the basic insert using object. + /// + [Test] + public void TestInsertBasicObject() + { + var cmd = new DynamicTypedInsertQueryBuilder(Database); + cmd.Values(x => new { Code = "001", Name = "Admin", IsAdmin = 1 }); + + Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0})", + string.Join(", ", cmd.Parameters.Keys.Select(p => string.Format("[${0}]", p)))), cmd.CommandText()); + } + + /// + /// Tests the insert using object with sub query. + /// + [Test] + public void TestInsertSubQueryObject() + { + var cmd = new DynamicTypedInsertQueryBuilder(Database); + + cmd.Values(x => new + { + Code = "001", + Name = "Admin", + IsAdmin = x(cmd + .SubQuery(a => a.AccessRights.As(a.a)) + .Select(a => a.IsAdmin) + .Where(a => a.User_Id == "001")) + }); + + Assert.AreEqual(string.Format(@"INSERT INTO ""Users"" (""Code"", ""Name"", ""IsAdmin"") VALUES ({0}, (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = [${1}])))", + string.Join(", ", cmd.Parameters.Keys.Take(2).Select(p => string.Format("[${0}]", p))), cmd.Parameters.Keys.Last()), cmd.CommandText()); + } + + #endregion Insert + + #region Update + + /// + /// Tests the basic update. + /// + [Test] + public void TestUpdateBasicSet() + { + var cmd = new DynamicTypedUpdateQueryBuilder(Database); + + cmd.Set(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = 1) + .Where(x => x.Users.Id_User == 1); + + Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = [${2}] WHERE (""Users"".""Id_User"" = [${3}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], cmd.Parameters.Keys.ToArray()[3]), cmd.CommandText()); + } + + /// + /// Tests the basic update. + /// + [Test] + public void TestUpdateBasicValues() + { + var cmd = new DynamicTypedUpdateQueryBuilder(Database); + + cmd + .Values("Code", "001") + .Values("Name", "Admin") + .Values("IsAdmin", "1") + .Where(x => x.Users.Id_User == 1); + + Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = [${2}] WHERE (""Users"".""Id_User"" = [${3}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], cmd.Parameters.Keys.ToArray()[3]), cmd.CommandText()); + } + + /// + /// Tests the insert with sub query. + /// + [Test] + public void TestUpdateSubQuery() + { + var cmd = new DynamicTypedUpdateQueryBuilder(Database); + cmd.Set(x => x.Users.Code = "001", x => x.Users.Name = "Admin", x => x.Users.IsAdmin = x(cmd + .SubQuery(a => a.AccessRights.As(a.a)) + .Select(a => a.IsAdmin) + .Where(a => a.User_Id == a.Users.Id_User))) + .Where(x => x.Users.Id_User == 1); + + Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = ""Users"".""Id_User"")) WHERE (""Users"".""Id_User"" = [${2}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + /// + /// Tests the basic insert using object. + /// + [Test] + public void TestUpdateBasicObject() + { + var cmd = new DynamicTypedUpdateQueryBuilder(Database); + + cmd.Set(x => new { Code = "001", Name = "Admin", IsAdmin = 1 }) + .Where(x => new { Id_User = 1 }); + + Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = [${2}] WHERE (""Id_User"" = [${3}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], cmd.Parameters.Keys.ToArray()[3]), cmd.CommandText()); + } + + /// + /// Tests the basic insert using object. + /// + [Test] + public void TestUpdateSubQueryObject() + { + var cmd = new DynamicTypedUpdateQueryBuilder(Database); + + cmd.Set(x => new + { + Code = "001", + Name = "Admin", + IsAdmin = x(cmd + .SubQuery(a => a.AccessRights.As(a.a)) + .Select(a => a.IsAdmin) + .Where(a => a.User_Id == a.Users.Id_User)) + }).Where(x => new { Id_User = 1 }); + + Assert.AreEqual(string.Format(@"UPDATE ""Users"" SET ""Code"" = [${0}], ""Name"" = [${1}], ""IsAdmin"" = (SELECT a.""IsAdmin"" FROM ""AccessRights"" AS a WHERE (a.""User_Id"" = ""Users"".""Id_User"")) WHERE (""Id_User"" = [${2}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + #endregion Update + } +} \ No newline at end of file diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index c8c16b8..968e120 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -26,7 +26,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -using System.Linq; using DynamORM.Builders; using DynamORM.Tests.Helpers; using NUnit.Framework; @@ -56,7 +55,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedWhereAndSelectUsesPropertyMap() { - var cmd = Database.From("u") + var cmd = Database.FromTyped("u") .Where(u => u.Id == 1) .Select(u => u.Code.As("CodeAlias")) .OrderBy(u => u.Code.Desc()); @@ -71,7 +70,7 @@ namespace DynamORM.Tests.Select { var ids = new[] { 1L, 2L, 3L }.ToList(); - var cmd = Database.From("u") + var cmd = Database.FromTyped("u") .Where(u => ids.Contains(u.Id)) .Select(u => u.Id); @@ -86,7 +85,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedGroupByHavingOrderBy() { - var cmd = Database.From("u") + var cmd = Database.FromTyped("u") .Select(u => u.Code) .GroupBy(u => u.Code) .Having(u => u.Code != null) @@ -99,7 +98,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedJoin() { - var cmd = Database.From("u") + var cmd = Database.FromTyped("u") .Join(j => j.As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); @@ -110,7 +109,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedLeftJoin() { - var cmd = Database.From("u") + var cmd = Database.FromTyped("u") .Join(j => j.Left().As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); @@ -121,8 +120,8 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedRightJoin() { - var cmd = Database.From("u") - .RightJoin((l, r) => l.Id == r.Id, "x") + var cmd = Database.FromTyped("u") + .Join(j => j.Right().As("X").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u RIGHT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", @@ -132,8 +131,8 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedFullJoin() { - var cmd = Database.From("u") - .FullJoin((l, r) => l.Id == r.Id, "x") + var cmd = Database.FromTyped("u") + .Join(j => j.Full().As("X").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u FULL JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", diff --git a/DynamORM.Tests/Select/TypedParserTests.cs b/DynamORM.Tests/Select/TypedParserTests.cs new file mode 100644 index 0000000..ae50f7e --- /dev/null +++ b/DynamORM.Tests/Select/TypedParserTests.cs @@ -0,0 +1,860 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, 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.Linq; +using DynamORM.Builders; +using DynamORM.Builders.Implementation; +using DynamORM.Tests.Helpers; +using NUnit.Framework; + +namespace DynamORM.Tests.Select +{ + /// + /// New parser tests. + /// + [TestFixture] + public class TypedParserTests : TestsBase + { + /// Setup test parameters. + [SetUp] + public virtual void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset | + DynamicDatabaseOptions.SupportNoLock); + } + + /// Tear down test objects. + [TearDown] + public virtual void TearDown() + { + try + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + catch { } + } + + + /// + /// Tests from typed method. + /// + [Test] + public void TestFromGetTyped() + { + var cmd = Database.From(); + + Assert.AreEqual("SELECT * FROM \"sample_users\"", cmd.CommandText()); + } + + /// + /// Tests from typed method. + /// + [Test] + public void TestFromGetTypedAs() + { + var cmd = Database.From("u"); + + Assert.AreEqual("SELECT * FROM \"sample_users\" AS u", cmd.CommandText()); + } + + /// + /// Tests from method using invoke with sub query. + /// + [Test] + public void TestFromSubQuery4() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u(new DynamicSelectQueryBuilder(Database).From(x => x.dbo.Users.NoLock())).As("u")); + + Assert.AreEqual("SELECT * FROM (SELECT * FROM \"dbo\".\"Users\" WITH(NOLOCK)) AS u", cmd.CommandText()); + } + + /// + /// Tests where method with alias. + /// + [Test] + public void TestWhereAlias() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.UserName == "admin"); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE (c.\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests where method with alias. + /// + [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()); + } + + /// + /// Tests complex where method with alias. + /// + [Test] + public void TestWhereAliasComplex() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.UserName == "admin" || u.c.UserName == "root") + .Where(u => u.c.IsActive = true); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND c.\"IsActive\" = ([${2}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + /// + /// Tests where method with alias using in. + /// + [Test] + public void TestWhereAliasIn() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + int[] ids = new int[] { 0, 1, 2, 3, 4, 5 }; + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.UserName == "admin" || u.c.UserName == "root") + .Where(u => u.c.IsActive == true) + .Where(u => u.c.Id_User.In(ids)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" IN({3})", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], + string.Join(", ", cmd.Parameters.Keys.Skip(3).Select(p => string.Format("[${0}]", p)))), cmd.CommandText()); + } + + /// + /// Tests where method with alias using between. + /// + [Test] + public void TestWhereAliasBetween1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + int[] ids = new int[] { 0, 5 }; + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.UserName == "admin" || u.c.UserName == "root") + .Where(u => u.c.IsActive == true) + .Where(u => u.c.Id_User.Between(ids)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" BETWEEN [${3}] AND [${4}]", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], + cmd.Parameters.Keys.ToArray()[3], cmd.Parameters.Keys.ToArray()[4]), cmd.CommandText()); + } + + /// + /// Tests where method with alias using between. + /// + [Test] + public void TestWhereAliasBetween2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + int[] ids = new int[] { 0, 5 }; + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.UserName == "admin" || u.c.UserName == "root") + .Where(u => u.c.IsActive == true) + .Where(u => u.c.Id_User.Between(ids[0], ids[1])); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE ((c.\"UserName\" = [${0}]) OR (c.\"UserName\" = [${1}])) AND (c.\"IsActive\" = [${2}]) AND c.\"Id_User\" BETWEEN [${3}] AND [${4}]", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2], + cmd.Parameters.Keys.ToArray()[3], cmd.Parameters.Keys.ToArray()[4]), cmd.CommandText()); + } + + /// + /// Tests where method without alias. + /// + [Test] + public void TestWhereNoAlias() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Where(u => u.UserName == "admin"); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" WHERE (\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests where method with full column name. + /// + [Test] + public void TestWhereNoAliasTableName() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Where(u => u.dbo.Users.UserName == "admin"); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" WHERE (\"dbo\".\"Users\".\"UserName\" = [${0}])", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests simple join method. + /// + [Test] + public void TestJoinClassic() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests inner join method. + /// + [Test] + public void TestInnerJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.Inner().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr INNER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests inner join method with aliases mix. + /// + [Test] + public void TestInnerJoin2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(usr => usr.Inner().dbo.UserClients.AS(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null)) + .Select(usr => usr.All(), uc => uc.Users); + + Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))"), cmd.CommandText()); + } + + /// + /// Tests from method using invoke with sub query. + /// + [Test] + public void TestInnerJoin3() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null))) + .Select(usr => usr.All(), uc => uc.Users); + + Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\") AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))"), cmd.CommandText()); + } + + /// + /// Tests from method using invoke with sub query. + /// + [Test] + public void TestInnerJoin4() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients).Where(x => x.Deleted == 0)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null))) + .Select(usr => usr.All(), uc => uc.Users); + + Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\" WHERE (\"Deleted\" = [${0}])) AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests from method using invoke with sub query an no lock. + /// + [Test] + public void TestInnerJoin5() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr).NoLock()) + .SubQuery((b, s) => b.Join(usr => usr(s.From(x => x.dbo.UserClients.NoLock()).Where(x => x.Deleted == 0)).Inner().As(usr.uc).On(usr.Id_User == usr.uc.User_Id && usr.uc.Users != null))) + .Select(usr => usr.All(), uc => uc.Users); + + Assert.AreEqual(string.Format("SELECT usr.*, uc.\"Users\" FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN (SELECT * FROM \"dbo\".\"UserClients\" WITH(NOLOCK) WHERE (\"Deleted\" = [${0}])) AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests inner join method with no lock. + /// + [Test] + public void TestInnerJoin6() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr).NoLock()) + .Join(u => u.Inner().dbo.UserClients.AS(u.uc).NoLock().On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN \"dbo\".\"UserClients\" AS uc WITH(NOLOCK) ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests left outer join method. + /// + [Test] + public void TestLeftOuterJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.LeftOuter().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests left join method. + /// + [Test] + public void TestLeftJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.Left().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests right outer join method. + /// + [Test] + public void TestRightOuterJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.RightOuter().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests right join method. + /// + [Test] + public void TestRightJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.Right().dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id)); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")"), cmd.CommandText()); + } + + /// + /// Tests complex join with parameters. + /// + [Test] + public void TestJoinClassicWithParamAndWhere() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.usr)) + .Join(u => u.dbo.UserClients.AS(u.uc).On(u.usr.Id_User == u.uc.User_Id && u.uc.Deleted == 0)) + .Where(u => u.usr.Active == true); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Deleted\" = [${0}])) WHERE (usr.\"Active\" = [${1}])", + cmd.Parameters.Keys.First(), cmd.Parameters.Keys.Last()), cmd.CommandText()); + } + + /// + /// Tests select all. + /// + [Test] + public void TestSelectAll1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.c.All()); + + Assert.AreEqual("SELECT c.* FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select all. + /// + [Test] + public void TestSelectAll2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Select(u => u.dbo.Users.All()); + + Assert.AreEqual("SELECT \"dbo\".\"Users\".* FROM \"dbo\".\"Users\"", cmd.CommandText()); + } + + /// + /// Tests select field. + /// + [Test] + public void TestSelectField1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.c.UserName); + + Assert.AreEqual("SELECT c.\"UserName\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select field. + /// + [Test] + public void TestSelectField2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Select(u => u.dbo.Users.UserName); + + Assert.AreEqual("SELECT \"dbo\".\"Users\".\"UserName\" FROM \"dbo\".\"Users\"", cmd.CommandText()); + } + + /// + /// Tests select field with alias. + /// + [Test] + public void TestSelectFieldAlias1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.c.UserName.As(u.Name)); + + Assert.AreEqual("SELECT c.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select field with alias. + /// + [Test] + public void TestSelectFieldAlias2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Select(u => u.dbo.Users.UserName.As(u.Name)); + + Assert.AreEqual("SELECT \"dbo\".\"Users\".\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\"", cmd.CommandText()); + } + + /// + /// Tests select field with alias. + /// + [Test] + public void TestSelectFieldAlias3() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.u)) + .Select(u => u.UserName.As(u.Name)); + + Assert.AreEqual("SELECT u.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS u", cmd.CommandText()); + } + + /// + /// Tests select field with alias. + /// + [Test] + public void TestSelectFieldAlias4() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.u)) + .Select(u => u.UserName.As(u.u.Name)); + + Assert.AreEqual("SELECT u.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS u", cmd.CommandText()); + } + + /// + /// Tests select aggregate field with alias (Sum). + /// + [Test] + public void TestSelectAggregateField1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.Sum(u.c.UserName).As(u.Name)); + + Assert.AreEqual("SELECT Sum(c.\"UserName\") AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select aggregate field with alias (Coalesce). + /// + [Test] + public void TestSelectAggregateField2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.Coalesce(u.c.UserName, u.c.FirstName + " " + u.c.LastName).As(u.Name)); + + Assert.AreEqual(string.Format("SELECT Coalesce(c.\"UserName\", ((c.\"FirstName\" + [${0}]) + c.\"LastName\")) AS \"Name\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests select aggregate field with alias (Sum). + /// + [Test] + public void TestSelectAggregateField3() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users) + .Select(u => u.Sum(u.dbo.Users.UserName)); + + Assert.AreEqual("SELECT Sum(\"dbo\".\"Users\".\"UserName\") FROM \"dbo\".\"Users\"", cmd.CommandText()); + } + + /// + /// Tests select aggregate field with alias (Sum). + /// + [Test] + public void TestSelectAggregateField4() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.Sum(u("\"UserName\"")).As(u.Name)); + + Assert.AreEqual("SELECT Sum(\"UserName\") AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select aggregate field with alias (Sum). + /// + [Test] + public void TestSelectAggregateField5() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u(u.Sum(u("\"UserName\"")), " + 1").As(u.Name)); + + Assert.AreEqual("SELECT Sum(\"UserName\") + 1 AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select from anonymous type. + /// + [Test] + public void TestSelectAnon() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => new + { + Id_User = u.c.Id_User, + Name = u.c.UserName, + }); + + Assert.AreEqual("SELECT c.\"Id_User\" AS \"Id_User\", c.\"UserName\" AS \"Name\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests select escaped case. + /// + [Test] + public void TestSelectCaseEscaped1() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u("CASE ", u.c.IsActive, " WHEN ", 1, " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted)); + + Assert.AreEqual(string.Format("SELECT CASE c.\"IsActive\" WHEN [${0}] THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + /// + /// Tests select escaped case. + /// + [Test] + public void TestSelectCaseEscaped2() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u("CASE WHEN ", u.c.IsActive == 1, " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted)); + + Assert.AreEqual(string.Format("SELECT CASE WHEN (c.\"IsActive\" = [${0}]) THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + /// + /// Tests select escaped case. + /// + [Test] + public void TestCoalesceEscaped() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u("COALESCE(", Database.DecorateName("ServerHash"), ", ", new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ")").As(u.Hash)); + + Assert.AreEqual(string.Format("SELECT COALESCE(\"ServerHash\", [${0}]) AS \"Hash\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText()); + } + + /// + /// Tests select escaped case. + /// + [Test] + public void TestCoalesce() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.Coalesce(u.c.ServerHash, new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }).As(u.Hash)); + + Assert.AreEqual(string.Format("SELECT Coalesce(c.\"ServerHash\", [${0}]) AS \"Hash\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.ToArray()[0]), cmd.CommandText()); + } + + /// + /// Tests select escaped case. + /// + [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(); + } + + /// + /// Tests select escaped case. + /// + [Test] + public void TestCoalesceInWhere() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u.ServerHash.As(u.Hash)) + .Where(u => u.Coalesce(u.c.ServerHash, new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) == new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + + Assert.AreEqual(string.Format("SELECT \"ServerHash\" AS \"Hash\" FROM \"dbo\".\"Users\" AS c WHERE (Coalesce(c.\"ServerHash\", [${0}]) = [${1}])", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1]), cmd.CommandText()); + } + + /// + /// Tests select escaped case with sub query. + /// + [Test] + public void TestSelectCaseEscapedAndSub() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u("CASE WHEN ", u.c.IsActive == 1, " AND ", u.c.IsAdmin == u(cmd.SubQuery() + .From(x => x.dbo.AccessRights.As(x.a)) + .Where(x => x.a.User_Id == x.c.Id_User) + .Select(x => x.a.IsAdmin)), " THEN ", 0, " ELSE ", 1, " END").As(u.Deleted)); + + Assert.AreEqual(string.Format("SELECT CASE WHEN (c.\"IsActive\" = [${0}]) AND (c.\"IsAdmin\" = (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\"))) THEN [${1}] ELSE [${2}] END AS \"Deleted\" FROM \"dbo\".\"Users\" AS c", + cmd.Parameters.Keys.ToArray()[0], cmd.Parameters.Keys.ToArray()[1], cmd.Parameters.Keys.ToArray()[2]), cmd.CommandText()); + } + + /// + /// Tests group by. + /// + [Test] + public void TestGroupBy() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .GroupBy(u => u.c.UserName); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c GROUP BY c.\"UserName\"", cmd.CommandText()); + } + + /// + /// Tests order by. + /// + [Test] + public void TestOrderBy() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .OrderBy(u => u.c.UserName); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY c.\"UserName\" ASC", cmd.CommandText()); + } + + /// + /// Tests order by using string with number. + /// + [Test] + public void TestOrderByNumberedColumnStr() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .OrderBy(u => "1 DESC"); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY 1 DESC", cmd.CommandText()); + } + + /// + /// Tests order by using member with number. + /// + [Test] + public void TestOrderByNumberedColFn() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .OrderBy(u => u.Desc(1)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY 1 DESC", cmd.CommandText()); + } + + /// + /// Tests order by using member with field. + /// + [Test] + public void TestOrderByAlt() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .OrderBy(u => u.Desc(u.c.UserName)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c ORDER BY c.\"UserName\" DESC", cmd.CommandText()); + } + + /// + /// Tests sub query select. + /// + [Test] + public void TestSubQuerySelect() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Select(u => u(cmd.SubQuery() + .From(x => x.dbo.AccessRights.As(x.a)) + .Where(x => x.a.User_Id == x.c.Id_User) + .Select(x => x.a.IsAdmin)).As(u.IsAdmin)); + + Assert.AreEqual("SELECT (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\")) AS \"IsAdmin\" FROM \"dbo\".\"Users\" AS c", cmd.CommandText()); + } + + /// + /// Tests sub query where. + /// + [Test] + public void TestSubQueryWhere() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.IsAdmin == u(cmd.SubQuery() + .From(x => x.dbo.AccessRights.As(x.a)) + .Where(x => x.a.User_Id == x.c.Id_User) + .Select(x => x.a.IsAdmin))); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c WHERE (c.\"IsAdmin\" = (SELECT a.\"IsAdmin\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"User_Id\" = c.\"Id_User\")))", cmd.CommandText()); + } + + /// + /// Tests sub query in. + /// + [Test] + public void TestSubQueryWhereIn() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Where(u => u.c.Id_User.In(u(cmd.SubQuery() + .From(x => x.dbo.AccessRights.As(x.a)) + .Where(x => x.a.IsAdmin == 1) + .Select(x => x.a.User_Id)))); + + Assert.AreEqual(string.Format("SELECT * FROM \"dbo\".\"Users\" AS c WHERE c.\"Id_User\" IN((SELECT a.\"User_Id\" FROM \"dbo\".\"AccessRights\" AS a WHERE (a.\"IsAdmin\" = [${0}])))", cmd.Parameters.Keys.First()), cmd.CommandText()); + } + + /// + /// Tests sub query join. + /// + [Test] + public void TestSubQueryJoin() + { + IDynamicSelectQueryBuilder cmd = new DynamicSelectQueryBuilder(Database); + + cmd.From(u => u.dbo.Users.As(u.c)) + .Join(u => u.Inner()(cmd.SubQuery() + .From(x => x.dbo.AccessRights.As(x.a)) + .Select(x => x.a.IsAdmin, x => x.a.User_Id)).As(u.ar).On(u.ar.User_Id == u.c.Id_User)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS c INNER JOIN (SELECT a.\"IsAdmin\", a.\"User_Id\" FROM \"dbo\".\"AccessRights\" AS a) AS ar ON (ar.\"User_Id\" = c.\"Id_User\")", cmd.CommandText()); + } + } +} \ No newline at end of file diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index bf4f420..237f30f 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -49,18 +49,6 @@ namespace DynamORM.Builders /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification); - /// Add INNER JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); - - /// Add LEFT JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null); - - /// Add RIGHT JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null); - - /// Add FULL JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null); - /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index 1e8053a..085b344 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -118,26 +118,6 @@ namespace DynamORM.Builders.Implementation return Join(spec.OnPredicate, spec.Alias, spec.JoinType); } - public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Inner); - } - - public IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Left); - } - - public IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Right); - } - - public IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Full); - } - public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) { base.Join(func); diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index a329123..a6e1250 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -453,28 +453,52 @@ namespace DynamORM /// Table alias. /// use no lock. /// This instance to permit chaining. - public virtual IDynamicTypedSelectQueryBuilder From(string alias = null, bool noLock = false) - { - // TODO: Make it more readable and maitainable - DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); - - if (noLock) - { - if (string.IsNullOrEmpty(alias)) - builder.From(x => x(typeof(T)).NoLock()); - else - builder.From(x => x(typeof(T)).As(alias).NoLock()); - } - else - { - if (string.IsNullOrEmpty(alias)) - builder.From(x => x(typeof(T))); - else - builder.From(x => x(typeof(T)).As(alias)); - } - - return builder; - } + public virtual IDynamicSelectQueryBuilder From(string alias = null, bool noLock = false) + { + // TODO: Make it more readable and maitainable + if (noLock) + { + if (string.IsNullOrEmpty(alias)) + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).NoLock()); + else + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias).NoLock()); + } + else + { + if (string.IsNullOrEmpty(alias)) + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T))); + else + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); + } + } + + /// Adds to the FROM clause using . + /// Type which can be represented in database. + /// Table alias. + /// use no lock. + /// This instance to permit chaining. + public virtual IDynamicTypedSelectQueryBuilder FromTyped(string alias = null, bool noLock = false) + { + // TODO: Make it more readable and maitainable + DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); + + if (noLock) + { + if (string.IsNullOrEmpty(alias)) + builder.From(x => x(typeof(T)).NoLock()); + else + builder.From(x => x(typeof(T)).As(alias).NoLock()); + } + else + { + if (string.IsNullOrEmpty(alias)) + builder.From(x => x(typeof(T))); + else + builder.From(x => x(typeof(T)).As(alias)); + } + + return builder; + } /// Adds to the FROM clause using . /// Type which can be represented in database. @@ -503,12 +527,20 @@ namespace DynamORM /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedInsertQueryBuilder Insert() - { - DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicInsertQueryBuilder Insert() + { + return new DynamicInsertQueryBuilder(this).Table(typeof(T)); + } + + /// Adds to the INSERT INTO clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedInsertQueryBuilder InsertTyped() + { + DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. @@ -612,12 +644,20 @@ namespace DynamORM /// Adds to the UPDATE clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedUpdateQueryBuilder Update() - { - DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicUpdateQueryBuilder Update() + { + return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); + } + + /// Adds to the UPDATE clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedUpdateQueryBuilder UpdateTyped() + { + DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the UPDATE clause using . /// Type which can be represented in database. @@ -835,12 +875,20 @@ namespace DynamORM /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedDeleteQueryBuilder Delete() - { - DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicDeleteQueryBuilder Delete() + { + return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); + } + + /// Adds to the DELETE FROM clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedDeleteQueryBuilder DeleteTyped() + { + DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); + builder.Table(typeof(T)); + return builder; + } /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. @@ -2016,16 +2064,16 @@ namespace DynamORM #region IExtendedDisposable Members - /// Performs application-defined tasks associated with freeing, - /// releasing, or resetting unmanaged resources. - public void Dispose() - { - if (IsDisposed) - return; - -#if !DYNAMORM_OMMIT_OLDSYNTAX - List tables = TablesCache.Values.ToList(); - TablesCache.Clear(); + /// Performs application-defined tasks associated with freeing, + /// releasing, or resetting unmanaged resources. + public void Dispose() + { + if (IsDisposed) + return; + +#if !DYNAMORM_OMMIT_OLDSYNTAX + List tables = TablesCache.Values.ToList(); + TablesCache.Clear(); tables.ForEach(t => t.Dispose()); tables.Clear(); @@ -2076,18 +2124,18 @@ namespace DynamORM RemainingBuilders = null; } - ClearSchema(); - if (_proc != null) - _proc.Dispose(); - - _proc = null; - _tempConn = null; - IsDisposed = true; - } + ClearSchema(); + if (_proc != null) + _proc.Dispose(); + + _proc = null; + _tempConn = null; + IsDisposed = true; + } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } #endregion IExtendedDisposable Members } -} +} From 59ce1115eaa68e981ec2c5d1b0c1794a6bac33ea Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 19:49:00 +0100 Subject: [PATCH 08/20] Expand typed join syntax with no lock, outer and custom join types --- AmalgamationTool/DynamORM.Amalgamation.cs | 253 ++++++++++++++++-- DynamORM.Tests/Helpers/TypedJoinModels.cs | 33 +++ .../Select/TypedFluentBuilderTests.cs | 2 +- .../Select/TypedFluentJoinSyntaxTests.cs | 102 +++++++ DynamORM/Builders/DynamicJoinType.cs | 6 +- .../IDynamicTypedSelectQueryBuilder.cs | 12 +- .../DynamicSelectQueryBuilder.cs | 2 +- .../DynamicTypedSelectQueryBuilder.cs | 160 +++++++++-- DynamORM/Builders/TypedJoinBuilder.cs | 76 +++++- DynamORM/Builders/TypedJoinExtensions.cs | 12 +- 10 files changed, 611 insertions(+), 47 deletions(-) create mode 100644 DynamORM.Tests/Helpers/TypedJoinModels.cs create mode 100644 DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 4f01c18..73602bb 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -6782,9 +6782,13 @@ namespace DynamORM public enum DynamicJoinType { Inner = 0, + Join, Left, Right, - Full + Full, + LeftOuter, + RightOuter, + FullOuter } /// Dynamic delete query builder interface. /// This interface it publicly available. Implementation should be hidden. @@ -7187,8 +7191,18 @@ namespace DynamORM /// Join ON predicate. /// Optional alias for joined table. /// Join type. + /// Adds NOLOCK hint to joined source when supported by provider options. /// Builder instance. - IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false); + + /// Add typed join with custom join type text (for example: CROSS APPLY). + /// Joined mapped entity type. + /// Optional join ON predicate. + /// Optional alias for joined table. + /// Join type text. + /// Adds NOLOCK hint to joined source when supported by provider options. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false); /// Add typed join using join-spec builder syntax (As(), join kind and On()). /// Joined mapped entity type. @@ -7415,7 +7429,7 @@ namespace DynamORM { internal TypedJoinBuilder() { - JoinType = DynamicJoinType.Inner; + JoinType = DynamicJoinType.Join; } /// Gets join alias. public string Alias { get; private set; } @@ -7423,9 +7437,18 @@ namespace DynamORM /// Gets join type. public DynamicJoinType JoinType { get; private set; } + /// Gets custom join type text. + public string CustomJoinType { get; private set; } + + /// Gets a value indicating whether joined source should use NOLOCK. + public bool UseNoLock { get; private set; } + /// Gets ON predicate. public Expression> OnPredicate { get; private set; } + /// Gets raw ON condition. + public string OnRawCondition { get; private set; } + /// Sets join alias. public TypedJoinBuilder As(string alias) { @@ -7436,24 +7459,71 @@ namespace DynamORM public TypedJoinBuilder Inner() { JoinType = DynamicJoinType.Inner; + CustomJoinType = null; + return this; + } + /// Sets plain JOIN. + public TypedJoinBuilder Join() + { + JoinType = DynamicJoinType.Join; + CustomJoinType = null; return this; } /// Sets LEFT JOIN. public TypedJoinBuilder Left() { JoinType = DynamicJoinType.Left; + CustomJoinType = null; return this; } /// Sets RIGHT JOIN. public TypedJoinBuilder Right() { JoinType = DynamicJoinType.Right; + CustomJoinType = null; return this; } /// Sets FULL JOIN. public TypedJoinBuilder Full() { JoinType = DynamicJoinType.Full; + CustomJoinType = null; + return this; + } + /// Sets LEFT OUTER JOIN. + public TypedJoinBuilder LeftOuter() + { + JoinType = DynamicJoinType.LeftOuter; + CustomJoinType = null; + return this; + } + /// Sets RIGHT OUTER JOIN. + public TypedJoinBuilder RightOuter() + { + JoinType = DynamicJoinType.RightOuter; + CustomJoinType = null; + return this; + } + /// Sets FULL OUTER JOIN. + public TypedJoinBuilder FullOuter() + { + JoinType = DynamicJoinType.FullOuter; + CustomJoinType = null; + return this; + } + /// Sets custom join type text (for example: CROSS APPLY). + public TypedJoinBuilder Type(string joinType) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); + + CustomJoinType = joinType.Trim(); + return this; + } + /// Marks joined source with NOLOCK hint. + public TypedJoinBuilder NoLock(bool use = true) + { + UseNoLock = use; return this; } /// Sets ON predicate. @@ -7463,6 +7533,17 @@ namespace DynamORM throw new ArgumentNullException("predicate"); OnPredicate = predicate; + OnRawCondition = null; + return this; + } + /// Sets raw ON clause (without the ON keyword). + public TypedJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnPredicate = null; return this; } } @@ -7484,12 +7565,22 @@ namespace DynamORM DynamicJoinType jt = DynamicJoinType.Inner; string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant(); - if (normalized == "LEFT JOIN" || normalized == "LEFT") + if (normalized == "JOIN") + jt = DynamicJoinType.Join; + else if (normalized == "LEFT OUTER JOIN" || normalized == "LEFT OUTER") + jt = DynamicJoinType.LeftOuter; + else if (normalized == "RIGHT OUTER JOIN" || normalized == "RIGHT OUTER") + jt = DynamicJoinType.RightOuter; + else if (normalized == "FULL OUTER JOIN" || normalized == "FULL OUTER") + jt = DynamicJoinType.FullOuter; + else if (normalized == "LEFT JOIN" || normalized == "LEFT") jt = DynamicJoinType.Left; else if (normalized == "RIGHT JOIN" || normalized == "RIGHT") jt = DynamicJoinType.Right; else if (normalized == "FULL JOIN" || normalized == "FULL") jt = DynamicJoinType.Full; + else if (normalized != "INNER JOIN" && normalized != "INNER" && normalized != "JOIN") + return builder.Join(on, alias, joinType); return builder.Join(on, alias, jt); } @@ -9223,7 +9314,7 @@ namespace DynamORM private string _select; private string _from; - private string _join; + protected string _join; private string _groupby; private string _orderby; @@ -10639,10 +10730,14 @@ namespace DynamORM return this; } - public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner) + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false) { - if (on == null) - throw new ArgumentNullException("on"); + return Join(on, alias, GetJoinKeyword(joinType), noLock); + } + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); if (rightMapper == null) @@ -10654,27 +10749,28 @@ namespace DynamORM string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (Tables.Count + 1).ToString() : alias; - BinaryExpression be = on.Body as BinaryExpression; - if (be == null || be.NodeType != ExpressionType.Equal) - throw new NotSupportedException("Typed join expression is currently limited to equality comparisons."); - string leftPrefix = GetRootAliasOrTableName(); if (string.IsNullOrEmpty(leftPrefix)) throw new InvalidOperationException("Join requires source table to be present."); - string leftExpr = ParseTypedJoinMember(be.Left, leftPrefix, _mapper); - string rightExpr = ParseTypedJoinMember(be.Right, rightAlias, rightMapper); + string condition = null; + if (on != null) + condition = ParseTypedJoinCondition(on.Body, leftPrefix, rightAlias, _mapper, rightMapper, on.Parameters[0], on.Parameters[1]); string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); - string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", - GetJoinKeyword(joinType), + string joinExpr = string.Format("{0} {1} AS {2}", + joinType.Trim(), rightTableExpr, - rightAlias, - leftExpr, - rightExpr); + rightAlias); - base.Join(x => joinExpr); + if (SupportNoLock && noLock) + joinExpr += " WITH(NOLOCK)"; + + if (!string.IsNullOrEmpty(condition)) + joinExpr += string.Format(" ON {0}", condition); + + AppendJoinClause(joinExpr); return this; } public IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification) @@ -10685,10 +10781,34 @@ namespace DynamORM TypedJoinBuilder spec = specification(new TypedJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - if (spec.OnPredicate == null) - throw new ArgumentException("Join specification must define ON predicate.", "specification"); + if (spec.OnPredicate != null) + return Join(spec.OnPredicate, spec.Alias, spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType), spec.UseNoLock); - return Join(spec.OnPredicate, spec.Alias, spec.JoinType); + DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) + ? typeof(TRight).Name + : rightMapper.Table.Name; + string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; + string rightAlias = string.IsNullOrEmpty(spec.Alias) ? "t" + (Tables.Count + 1).ToString() : spec.Alias; + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2}", + (spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType)).Trim(), + rightTableExpr, + rightAlias); + + if (SupportNoLock && spec.UseNoLock) + joinExpr += " WITH(NOLOCK)"; + + if (!string.IsNullOrEmpty(spec.OnRawCondition)) + joinExpr += string.Format(" ON {0}", spec.OnRawCondition); + + AppendJoinClause(joinExpr); + return this; } public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) { @@ -11061,16 +11181,82 @@ namespace DynamORM { switch (joinType) { + case DynamicJoinType.Join: + return "JOIN"; case DynamicJoinType.Left: return "LEFT JOIN"; case DynamicJoinType.Right: return "RIGHT JOIN"; case DynamicJoinType.Full: return "FULL JOIN"; + case DynamicJoinType.LeftOuter: + return "LEFT OUTER JOIN"; + case DynamicJoinType.RightOuter: + return "RIGHT OUTER JOIN"; + case DynamicJoinType.FullOuter: + return "FULL OUTER JOIN"; default: return "INNER JOIN"; } } + private void AppendJoinClause(string joinClause) + { + _join = string.IsNullOrEmpty(_join) + ? joinClause + : string.Format("{0} {1}", _join, joinClause); + } + private string ParseTypedJoinCondition(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) + { + expression = UnwrapConvert(expression); + + if (expression is BinaryExpression binary) + { + switch (binary.NodeType) + { + case ExpressionType.AndAlso: + case ExpressionType.And: + return string.Format("({0} AND {1})", + ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), + ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + case ExpressionType.OrElse: + case ExpressionType.Or: + return string.Format("({0} OR {1})", + ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), + ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + { + string left = ParseTypedJoinValue(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); + string right = ParseTypedJoinValue(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); + string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); + return string.Format("({0} {1} {2})", left, op, right); + } + } + } + if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) + return string.Format("(NOT {0})", ParseTypedJoinCondition(unary.Operand, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + + throw new NotSupportedException(string.Format("Typed join condition is not supported: {0}", expression)); + } + private string ParseTypedJoinValue(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member != null && member.Expression is ParameterExpression parameter) + { + if (parameter == leftParameter) + return ParseTypedJoinMemberByMapper(member, leftPrefix, leftMapper); + if (parameter == rightParameter) + return ParseTypedJoinMemberByMapper(member, rightPrefix, rightMapper); + } + DynamicSchemaColumn? col = null; + object value = EvaluateExpression(expression); + return ParseConstant(value, Parameters, col); + } private string ParseTypedJoinMember(Expression expression, string tablePrefix, DynamicTypeMap mapper) { expression = UnwrapConvert(expression); @@ -11097,6 +11283,27 @@ namespace DynamORM return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); } + private string ParseTypedJoinMemberByMapper(MemberExpression member, string tablePrefix, DynamicTypeMap mapper) + { + string mappedColumn = null; + PropertyInfo property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + if (string.IsNullOrEmpty(mappedColumn)) + mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); + } private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression unary && diff --git a/DynamORM.Tests/Helpers/TypedJoinModels.cs b/DynamORM.Tests/Helpers/TypedJoinModels.cs new file mode 100644 index 0000000..e29b31a --- /dev/null +++ b/DynamORM.Tests/Helpers/TypedJoinModels.cs @@ -0,0 +1,33 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using DynamORM.Mapper; + +namespace DynamORM.Tests.Helpers +{ + [Table(Name = "Users", Owner = "dbo")] + public class TypedJoinUser + { + [Column("Id_User", true)] + public long IdUser { get; set; } + + [Column("Active")] + public int Active { get; set; } + } + + [Table(Name = "UserClients", Owner = "dbo")] + public class TypedJoinUserClient + { + [Column("User_Id", true)] + public long UserId { get; set; } + + [Column("Users")] + public string Users { get; set; } + + [Column("Deleted")] + public int Deleted { get; set; } + } +} diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index 968e120..84ff65a 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -99,7 +99,7 @@ namespace DynamORM.Tests.Select public void TestTypedJoin() { var cmd = Database.FromTyped("u") - .Join(j => j.As("x").On((l, r) => l.Id == r.Id)) + .Join(j => j.Inner().As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u INNER JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", diff --git a/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs b/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs new file mode 100644 index 0000000..4c29f68 --- /dev/null +++ b/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs @@ -0,0 +1,102 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System.Linq; +using DynamORM.Tests.Helpers; +using NUnit.Framework; + +namespace DynamORM.Tests.Select +{ + [TestFixture] + public class TypedFluentJoinSyntaxTests : TestsBase + { + [SetUp] + public void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportNoLock); + } + + [TearDown] + public void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + [Test] + public void TestTypedJoinDefaultJoinKeyword() + { + var cmd = Database.From("usr") + .Join(j => j.As("uc").On((u, c) => u.IdUser == c.UserId)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); + } + + [Test] + public void TestTypedInnerJoinWithAndNull() + { + var cmd = Database.From("usr") + .Join(j => j.Inner().As("uc").On((u, c) => u.IdUser == c.UserId && c.Users != null)) + .Select(u => u.IdUser); + + Assert.AreEqual("SELECT usr.\"Id_User\" FROM \"dbo\".\"Users\" AS usr INNER JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Users\" IS NOT NULL))", cmd.CommandText()); + } + + [Test] + public void TestTypedJoinWithNoLock() + { + var cmd = Database.From("usr", noLock: true) + .Join(j => j.Inner().As("uc").NoLock().On((u, c) => u.IdUser == c.UserId)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN \"dbo\".\"UserClients\" AS uc WITH(NOLOCK) ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); + } + + [Test] + public void TestTypedLeftOuterJoin() + { + var cmd = Database.From("usr") + .Join(j => j.LeftOuter().As("uc").On((u, c) => u.IdUser == c.UserId)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); + } + + [Test] + public void TestTypedRightOuterJoin() + { + var cmd = Database.From("usr") + .Join(j => j.RightOuter().As("uc").On((u, c) => u.IdUser == c.UserId)); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); + } + + [Test] + public void TestTypedCustomJoinTypeCrossApply() + { + var cmd = Database.From("usr") + .Join(j => j.Type("CROSS APPLY").As("uc")); + + Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr CROSS APPLY \"dbo\".\"UserClients\" AS uc", cmd.CommandText()); + } + + [Test] + public void TestTypedJoinAndWhereParameterOrder() + { + var cmd = Database.From("usr") + .Join(j => j.As("uc").On((u, c) => u.IdUser == c.UserId && c.Deleted == 0)) + .Where(u => u.Active == 1); + + Assert.AreEqual( + string.Format("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON ((usr.\"Id_User\" = uc.\"User_Id\") AND (uc.\"Deleted\" = [${0}])) WHERE (usr.\"Active\" = [${1}])", + cmd.Parameters.Keys.ElementAt(0), + cmd.Parameters.Keys.ElementAt(1)), + cmd.CommandText()); + } + } +} diff --git a/DynamORM/Builders/DynamicJoinType.cs b/DynamORM/Builders/DynamicJoinType.cs index a4da19f..b81a498 100644 --- a/DynamORM/Builders/DynamicJoinType.cs +++ b/DynamORM/Builders/DynamicJoinType.cs @@ -10,8 +10,12 @@ namespace DynamORM.Builders public enum DynamicJoinType { Inner = 0, + Join, Left, Right, - Full + Full, + LeftOuter, + RightOuter, + FullOuter } } diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index 237f30f..4a637a5 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -40,8 +40,18 @@ namespace DynamORM.Builders /// Join ON predicate. /// Optional alias for joined table. /// Join type. + /// Adds NOLOCK hint to joined source when supported by provider options. /// Builder instance. - IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner); + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false); + + /// Add typed join with custom join type text (for example: CROSS APPLY). + /// Joined mapped entity type. + /// Optional join ON predicate. + /// Optional alias for joined table. + /// Join type text. + /// Adds NOLOCK hint to joined source when supported by provider options. + /// Builder instance. + IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false); /// Add typed join using join-spec builder syntax (As(), join kind and On()). /// Joined mapped entity type. diff --git a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs index 387f54b..f2bae87 100644 --- a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs @@ -50,7 +50,7 @@ namespace DynamORM.Builders.Implementation private string _select; private string _from; - private string _join; + protected string _join; private string _groupby; private string _orderby; diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index 085b344..af340e0 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -65,10 +65,15 @@ namespace DynamORM.Builders.Implementation return this; } - public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner) + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false) { - if (on == null) - throw new ArgumentNullException("on"); + return Join(on, alias, GetJoinKeyword(joinType), noLock); + } + + public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); if (rightMapper == null) @@ -80,27 +85,28 @@ namespace DynamORM.Builders.Implementation string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (Tables.Count + 1).ToString() : alias; - BinaryExpression be = on.Body as BinaryExpression; - if (be == null || be.NodeType != ExpressionType.Equal) - throw new NotSupportedException("Typed join expression is currently limited to equality comparisons."); - string leftPrefix = GetRootAliasOrTableName(); if (string.IsNullOrEmpty(leftPrefix)) throw new InvalidOperationException("Join requires source table to be present."); - string leftExpr = ParseTypedJoinMember(be.Left, leftPrefix, _mapper); - string rightExpr = ParseTypedJoinMember(be.Right, rightAlias, rightMapper); + string condition = null; + if (on != null) + condition = ParseTypedJoinCondition(on.Body, leftPrefix, rightAlias, _mapper, rightMapper, on.Parameters[0], on.Parameters[1]); string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); - string joinExpr = string.Format("{0} {1} AS {2} ON ({3} = {4})", - GetJoinKeyword(joinType), + string joinExpr = string.Format("{0} {1} AS {2}", + joinType.Trim(), rightTableExpr, - rightAlias, - leftExpr, - rightExpr); + rightAlias); - base.Join(x => joinExpr); + if (SupportNoLock && noLock) + joinExpr += " WITH(NOLOCK)"; + + if (!string.IsNullOrEmpty(condition)) + joinExpr += string.Format(" ON {0}", condition); + + AppendJoinClause(joinExpr); return this; } @@ -112,10 +118,34 @@ namespace DynamORM.Builders.Implementation TypedJoinBuilder spec = specification(new TypedJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - if (spec.OnPredicate == null) - throw new ArgumentException("Join specification must define ON predicate.", "specification"); + if (spec.OnPredicate != null) + return Join(spec.OnPredicate, spec.Alias, spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType), spec.UseNoLock); - return Join(spec.OnPredicate, spec.Alias, spec.JoinType); + DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); + if (rightMapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); + + string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) + ? typeof(TRight).Name + : rightMapper.Table.Name; + string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; + string rightAlias = string.IsNullOrEmpty(spec.Alias) ? "t" + (Tables.Count + 1).ToString() : spec.Alias; + + string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; + string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); + string joinExpr = string.Format("{0} {1} AS {2}", + (spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType)).Trim(), + rightTableExpr, + rightAlias); + + if (SupportNoLock && spec.UseNoLock) + joinExpr += " WITH(NOLOCK)"; + + if (!string.IsNullOrEmpty(spec.OnRawCondition)) + joinExpr += string.Format(" ON {0}", spec.OnRawCondition); + + AppendJoinClause(joinExpr); + return this; } public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) @@ -511,17 +541,88 @@ namespace DynamORM.Builders.Implementation { switch (joinType) { + case DynamicJoinType.Join: + return "JOIN"; case DynamicJoinType.Left: return "LEFT JOIN"; case DynamicJoinType.Right: return "RIGHT JOIN"; case DynamicJoinType.Full: return "FULL JOIN"; + case DynamicJoinType.LeftOuter: + return "LEFT OUTER JOIN"; + case DynamicJoinType.RightOuter: + return "RIGHT OUTER JOIN"; + case DynamicJoinType.FullOuter: + return "FULL OUTER JOIN"; default: return "INNER JOIN"; } } + private void AppendJoinClause(string joinClause) + { + _join = string.IsNullOrEmpty(_join) + ? joinClause + : string.Format("{0} {1}", _join, joinClause); + } + + private string ParseTypedJoinCondition(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) + { + expression = UnwrapConvert(expression); + + if (expression is BinaryExpression binary) + { + switch (binary.NodeType) + { + case ExpressionType.AndAlso: + case ExpressionType.And: + return string.Format("({0} AND {1})", + ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), + ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + case ExpressionType.OrElse: + case ExpressionType.Or: + return string.Format("({0} OR {1})", + ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), + ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + { + string left = ParseTypedJoinValue(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); + string right = ParseTypedJoinValue(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); + string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); + return string.Format("({0} {1} {2})", left, op, right); + } + } + } + + if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) + return string.Format("(NOT {0})", ParseTypedJoinCondition(unary.Operand, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); + + throw new NotSupportedException(string.Format("Typed join condition is not supported: {0}", expression)); + } + + private string ParseTypedJoinValue(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) + { + expression = UnwrapConvert(expression); + MemberExpression member = expression as MemberExpression; + if (member != null && member.Expression is ParameterExpression parameter) + { + if (parameter == leftParameter) + return ParseTypedJoinMemberByMapper(member, leftPrefix, leftMapper); + if (parameter == rightParameter) + return ParseTypedJoinMemberByMapper(member, rightPrefix, rightMapper); + } + + DynamicSchemaColumn? col = null; + object value = EvaluateExpression(expression); + return ParseConstant(value, Parameters, col); + } + private string ParseTypedJoinMember(Expression expression, string tablePrefix, DynamicTypeMap mapper) { expression = UnwrapConvert(expression); @@ -550,6 +651,29 @@ namespace DynamORM.Builders.Implementation return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); } + private string ParseTypedJoinMemberByMapper(MemberExpression member, string tablePrefix, DynamicTypeMap mapper) + { + string mappedColumn = null; + PropertyInfo property = member.Member as PropertyInfo; + if (property != null) + { + var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); + ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); + if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) + mappedColumn = colAttr.Name; + } + + if (string.IsNullOrEmpty(mappedColumn)) + mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) + ?? mapper.PropertyMap + .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value) + .FirstOrDefault() + ?? member.Member.Name; + + return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); + } + private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression unary && diff --git a/DynamORM/Builders/TypedJoinBuilder.cs b/DynamORM/Builders/TypedJoinBuilder.cs index ebc5bb3..ac0440b 100644 --- a/DynamORM/Builders/TypedJoinBuilder.cs +++ b/DynamORM/Builders/TypedJoinBuilder.cs @@ -16,7 +16,7 @@ namespace DynamORM.Builders { internal TypedJoinBuilder() { - JoinType = DynamicJoinType.Inner; + JoinType = DynamicJoinType.Join; } /// Gets join alias. @@ -25,9 +25,18 @@ namespace DynamORM.Builders /// Gets join type. public DynamicJoinType JoinType { get; private set; } + /// Gets custom join type text. + public string CustomJoinType { get; private set; } + + /// Gets a value indicating whether joined source should use NOLOCK. + public bool UseNoLock { get; private set; } + /// Gets ON predicate. public Expression> OnPredicate { get; private set; } + /// Gets raw ON condition. + public string OnRawCondition { get; private set; } + /// Sets join alias. public TypedJoinBuilder As(string alias) { @@ -39,6 +48,15 @@ namespace DynamORM.Builders public TypedJoinBuilder Inner() { JoinType = DynamicJoinType.Inner; + CustomJoinType = null; + return this; + } + + /// Sets plain JOIN. + public TypedJoinBuilder Join() + { + JoinType = DynamicJoinType.Join; + CustomJoinType = null; return this; } @@ -46,6 +64,7 @@ namespace DynamORM.Builders public TypedJoinBuilder Left() { JoinType = DynamicJoinType.Left; + CustomJoinType = null; return this; } @@ -53,6 +72,7 @@ namespace DynamORM.Builders public TypedJoinBuilder Right() { JoinType = DynamicJoinType.Right; + CustomJoinType = null; return this; } @@ -60,6 +80,48 @@ namespace DynamORM.Builders public TypedJoinBuilder Full() { JoinType = DynamicJoinType.Full; + CustomJoinType = null; + return this; + } + + /// Sets LEFT OUTER JOIN. + public TypedJoinBuilder LeftOuter() + { + JoinType = DynamicJoinType.LeftOuter; + CustomJoinType = null; + return this; + } + + /// Sets RIGHT OUTER JOIN. + public TypedJoinBuilder RightOuter() + { + JoinType = DynamicJoinType.RightOuter; + CustomJoinType = null; + return this; + } + + /// Sets FULL OUTER JOIN. + public TypedJoinBuilder FullOuter() + { + JoinType = DynamicJoinType.FullOuter; + CustomJoinType = null; + return this; + } + + /// Sets custom join type text (for example: CROSS APPLY). + public TypedJoinBuilder Type(string joinType) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); + + CustomJoinType = joinType.Trim(); + return this; + } + + /// Marks joined source with NOLOCK hint. + public TypedJoinBuilder NoLock(bool use = true) + { + UseNoLock = use; return this; } @@ -70,6 +132,18 @@ namespace DynamORM.Builders throw new ArgumentNullException("predicate"); OnPredicate = predicate; + OnRawCondition = null; + return this; + } + + /// Sets raw ON clause (without the ON keyword). + public TypedJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnPredicate = null; return this; } } diff --git a/DynamORM/Builders/TypedJoinExtensions.cs b/DynamORM/Builders/TypedJoinExtensions.cs index c3b5d07..1c8e7ce 100644 --- a/DynamORM/Builders/TypedJoinExtensions.cs +++ b/DynamORM/Builders/TypedJoinExtensions.cs @@ -27,12 +27,22 @@ namespace DynamORM.Builders DynamicJoinType jt = DynamicJoinType.Inner; string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant(); - if (normalized == "LEFT JOIN" || normalized == "LEFT") + if (normalized == "JOIN") + jt = DynamicJoinType.Join; + else if (normalized == "LEFT OUTER JOIN" || normalized == "LEFT OUTER") + jt = DynamicJoinType.LeftOuter; + else if (normalized == "RIGHT OUTER JOIN" || normalized == "RIGHT OUTER") + jt = DynamicJoinType.RightOuter; + else if (normalized == "FULL OUTER JOIN" || normalized == "FULL OUTER") + jt = DynamicJoinType.FullOuter; + else if (normalized == "LEFT JOIN" || normalized == "LEFT") jt = DynamicJoinType.Left; else if (normalized == "RIGHT JOIN" || normalized == "RIGHT") jt = DynamicJoinType.Right; else if (normalized == "FULL JOIN" || normalized == "FULL") jt = DynamicJoinType.Full; + else if (normalized != "INNER JOIN" && normalized != "INNER" && normalized != "JOIN") + return builder.Join(on, alias, joinType); return builder.Join(on, alias, jt); } From 3d430b0124f623e3c071b7a498848d6e5f57f60b Mon Sep 17 00:00:00 2001 From: root Date: Thu, 26 Feb 2026 19:54:37 +0100 Subject: [PATCH 09/20] Fix typed builder compatibility after rebase --- AmalgamationTool/DynamORM.Amalgamation.cs | 116 +++++++++++------- .../Select/TypedFluentBuilderTests.cs | 4 +- .../Select/TypedFluentJoinSyntaxTests.cs | 14 +-- .../DynamicTypedDeleteQueryBuilder.cs | 10 ++ .../DynamicTypedInsertQueryBuilder.cs | 10 ++ .../DynamicTypedUpdateQueryBuilder.cs | 10 ++ DynamORM/DynamicDatabase.cs | 30 ++--- 7 files changed, 126 insertions(+), 68 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 73602bb..39740b5 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -1932,7 +1932,30 @@ namespace DynamORM /// Table alias. /// use no lock. /// This instance to permit chaining. - public virtual IDynamicTypedSelectQueryBuilder From(string alias = null, bool noLock = false) + public virtual IDynamicSelectQueryBuilder From(string alias = null, bool noLock = false) + { + // TODO: Make it more readable and maitainable + if (noLock) + { + if (string.IsNullOrEmpty(alias)) + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).NoLock()); + else + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias).NoLock()); + } + else + { + if (string.IsNullOrEmpty(alias)) + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T))); + else + return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); + } + } + /// Adds to the FROM clause using . + /// Type which can be represented in database. + /// Table alias. + /// use no lock. + /// This instance to permit chaining. + public virtual IDynamicTypedSelectQueryBuilder FromTyped(string alias = null, bool noLock = false) { // TODO: Make it more readable and maitainable DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); @@ -1978,11 +2001,16 @@ namespace DynamORM /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedInsertQueryBuilder Insert() + public virtual IDynamicInsertQueryBuilder Insert() { - DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); - builder.Table(typeof(T)); - return builder; + return new DynamicInsertQueryBuilder(this).Table(typeof(T)); + } + /// Adds to the INSERT INTO clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedInsertQueryBuilder InsertTyped() + { + return new DynamicTypedInsertQueryBuilder(this, true); } /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. @@ -2080,11 +2108,16 @@ namespace DynamORM /// Adds to the UPDATE clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedUpdateQueryBuilder Update() + public virtual IDynamicUpdateQueryBuilder Update() { - DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); - builder.Table(typeof(T)); - return builder; + return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); + } + /// Adds to the UPDATE clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedUpdateQueryBuilder UpdateTyped() + { + return new DynamicTypedUpdateQueryBuilder(this, true); } /// Adds to the UPDATE clause using . /// Type which can be represented in database. @@ -2291,11 +2324,16 @@ namespace DynamORM /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedDeleteQueryBuilder Delete() + public virtual IDynamicDeleteQueryBuilder Delete() { - DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); - builder.Table(typeof(T)); - return builder; + return new DynamicDeleteQueryBuilder(this).Table(typeof(T)); + } + /// Adds to the DELETE FROM clause using . + /// Type which can be represented in database. + /// This instance to permit chaining. + public virtual IDynamicTypedDeleteQueryBuilder DeleteTyped() + { + return new DynamicTypedDeleteQueryBuilder(this, true); } /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. @@ -7210,18 +7248,6 @@ namespace DynamORM /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification); - /// Add INNER JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null); - - /// Add LEFT JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null); - - /// Add RIGHT JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null); - - /// Add FULL JOIN using typed ON predicate. - IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null); - /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. @@ -10636,8 +10662,16 @@ namespace DynamORM internal class DynamicTypedDeleteQueryBuilder : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder { internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { @@ -10675,8 +10709,16 @@ namespace DynamORM internal class DynamicTypedInsertQueryBuilder : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder { internal DynamicTypedInsertQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + internal DynamicTypedInsertQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { @@ -10810,22 +10852,6 @@ namespace DynamORM AppendJoinClause(joinExpr); return this; } - public IDynamicTypedSelectQueryBuilder InnerJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Inner); - } - public IDynamicTypedSelectQueryBuilder LeftJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Left); - } - public IDynamicTypedSelectQueryBuilder RightJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Right); - } - public IDynamicTypedSelectQueryBuilder FullJoin(Expression> on, string alias = null) - { - return Join(on, alias, DynamicJoinType.Full); - } public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) { base.Join(func); @@ -11363,8 +11389,16 @@ namespace DynamORM internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder { internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { diff --git a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs index 84ff65a..d479c38 100644 --- a/DynamORM.Tests/Select/TypedFluentBuilderTests.cs +++ b/DynamORM.Tests/Select/TypedFluentBuilderTests.cs @@ -121,7 +121,7 @@ namespace DynamORM.Tests.Select public void TestTypedRightJoin() { var cmd = Database.FromTyped("u") - .Join(j => j.Right().As("X").On((l, r) => l.Id == r.Id)) + .Join(j => j.Right().As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u RIGHT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", @@ -132,7 +132,7 @@ namespace DynamORM.Tests.Select public void TestTypedFullJoin() { var cmd = Database.FromTyped("u") - .Join(j => j.Full().As("X").On((l, r) => l.Id == r.Id)) + .Join(j => j.Full().As("x").On((l, r) => l.Id == r.Id)) .Select(u => u.Id); Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u FULL JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", diff --git a/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs b/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs index 4c29f68..d11945a 100644 --- a/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs +++ b/DynamORM.Tests/Select/TypedFluentJoinSyntaxTests.cs @@ -33,7 +33,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedJoinDefaultJoinKeyword() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.As("uc").On((u, c) => u.IdUser == c.UserId)); Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); @@ -42,7 +42,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedInnerJoinWithAndNull() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.Inner().As("uc").On((u, c) => u.IdUser == c.UserId && c.Users != null)) .Select(u => u.IdUser); @@ -52,7 +52,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedJoinWithNoLock() { - var cmd = Database.From("usr", noLock: true) + var cmd = Database.FromTyped("usr", noLock: true) .Join(j => j.Inner().As("uc").NoLock().On((u, c) => u.IdUser == c.UserId)); Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr WITH(NOLOCK) INNER JOIN \"dbo\".\"UserClients\" AS uc WITH(NOLOCK) ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); @@ -61,7 +61,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedLeftOuterJoin() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.LeftOuter().As("uc").On((u, c) => u.IdUser == c.UserId)); Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr LEFT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); @@ -70,7 +70,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedRightOuterJoin() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.RightOuter().As("uc").On((u, c) => u.IdUser == c.UserId)); Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr RIGHT OUTER JOIN \"dbo\".\"UserClients\" AS uc ON (usr.\"Id_User\" = uc.\"User_Id\")", cmd.CommandText()); @@ -79,7 +79,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedCustomJoinTypeCrossApply() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.Type("CROSS APPLY").As("uc")); Assert.AreEqual("SELECT * FROM \"dbo\".\"Users\" AS usr CROSS APPLY \"dbo\".\"UserClients\" AS uc", cmd.CommandText()); @@ -88,7 +88,7 @@ namespace DynamORM.Tests.Select [Test] public void TestTypedJoinAndWhereParameterOrder() { - var cmd = Database.From("usr") + var cmd = Database.FromTyped("usr") .Join(j => j.As("uc").On((u, c) => u.IdUser == c.UserId && c.Deleted == 0)) .Where(u => u.Active == 1); diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs index eddbd13..f475f95 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.Builders.Extensions; namespace DynamORM.Builders.Implementation { @@ -14,8 +15,17 @@ namespace DynamORM.Builders.Implementation internal class DynamicTypedDeleteQueryBuilder : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder { internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + + internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs index 257f7ee..2ae6ee1 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.Builders.Extensions; namespace DynamORM.Builders.Implementation { @@ -14,8 +15,17 @@ namespace DynamORM.Builders.Implementation internal class DynamicTypedInsertQueryBuilder : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder { internal DynamicTypedInsertQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + + internal DynamicTypedInsertQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs index efa2c26..6b6720d 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.Builders.Extensions; namespace DynamORM.Builders.Implementation { @@ -14,8 +15,17 @@ namespace DynamORM.Builders.Implementation internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder { internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db) + : this(db, false) + { + } + + internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { + if (mapType) + this.Table(typeof(T)); + else + this.Table(typeof(T).Name); } public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index a6e1250..83b4e9b 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -535,12 +535,10 @@ namespace DynamORM /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedInsertQueryBuilder InsertTyped() - { - DynamicTypedInsertQueryBuilder builder = new DynamicTypedInsertQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicTypedInsertQueryBuilder InsertTyped() + { + return new DynamicTypedInsertQueryBuilder(this, true); + } /// Adds to the INSERT INTO clause using . /// Type which can be represented in database. @@ -652,12 +650,10 @@ namespace DynamORM /// Adds to the UPDATE clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedUpdateQueryBuilder UpdateTyped() - { - DynamicTypedUpdateQueryBuilder builder = new DynamicTypedUpdateQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicTypedUpdateQueryBuilder UpdateTyped() + { + return new DynamicTypedUpdateQueryBuilder(this, true); + } /// Adds to the UPDATE clause using . /// Type which can be represented in database. @@ -883,12 +879,10 @@ namespace DynamORM /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. - public virtual IDynamicTypedDeleteQueryBuilder DeleteTyped() - { - DynamicTypedDeleteQueryBuilder builder = new DynamicTypedDeleteQueryBuilder(this); - builder.Table(typeof(T)); - return builder; - } + public virtual IDynamicTypedDeleteQueryBuilder DeleteTyped() + { + return new DynamicTypedDeleteQueryBuilder(this, true); + } /// Adds to the DELETE FROM clause using . /// Type which can be represented in database. From 8b1737a53bc3e982b446ec6776273c43dd0d271d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 08:22:23 +0100 Subject: [PATCH 10/20] Add experimental typed SQL DSL for typed builders --- AmalgamationTool/DynamORM.Amalgamation.cs | 705 +++++++++++++++++- DynamORM.Tests/TypedSql/TypedSqlDslTests.cs | 119 +++ .../IDynamicTypedDeleteQueryBuilder.cs | 4 + .../IDynamicTypedInsertQueryBuilder.cs | 4 + .../IDynamicTypedSelectQueryBuilder.cs | 16 + .../IDynamicTypedUpdateQueryBuilder.cs | 7 + .../DynamicInsertQueryBuilder.cs | 6 +- .../DynamicSelectQueryBuilder.cs | 8 +- .../DynamicTypedDeleteQueryBuilder.cs | 21 + .../DynamicTypedInsertQueryBuilder.cs | 20 + .../DynamicTypedSelectQueryBuilder.cs | 153 +++- .../DynamicTypedUpdateQueryBuilder.cs | 30 + .../DynamicUpdateQueryBuilder.cs | 4 +- .../Implementation/TypedModifyHelper.cs | 76 ++ DynamORM/Builders/TypedJoinBuilder.cs | 18 + DynamORM/TypedSql/ITypedSqlRenderContext.cs | 23 + DynamORM/TypedSql/Sql.cs | 91 +++ DynamORM/TypedSql/TypedSqlExpression.cs | 321 ++++++++ DynamORM/TypedSql/TypedTableContext.cs | 40 + 19 files changed, 1649 insertions(+), 17 deletions(-) create mode 100644 DynamORM.Tests/TypedSql/TypedSqlDslTests.cs create mode 100644 DynamORM/TypedSql/ITypedSqlRenderContext.cs create mode 100644 DynamORM/TypedSql/Sql.cs create mode 100644 DynamORM/TypedSql/TypedSqlExpression.cs create mode 100644 DynamORM/TypedSql/TypedTableContext.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 39740b5..212eb91 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -10,6 +10,7 @@ using DynamORM.Builders; using DynamORM.Helpers.Dynamics; using DynamORM.Helpers; using DynamORM.Mapper; +using DynamORM.TypedSql; using DynamORM.Validation; using System.Collections.Concurrent; using System.Collections.Generic; @@ -7203,6 +7204,9 @@ namespace DynamORM /// Predicate to parse. /// Builder instance. IDynamicTypedDeleteQueryBuilder Where(Expression> predicate); + + /// Add typed SQL DSL where predicate. + IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); } /// Typed insert query builder for mapped entities. /// Mapped entity type. @@ -7219,6 +7223,9 @@ namespace DynamORM /// Mapped object value. /// Builder instance. IDynamicTypedInsertQueryBuilder Insert(T value); + + /// Add typed SQL DSL insert assignment. + IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory); } /// Typed select query builder for mapped entities. /// Mapped entity type. @@ -7278,6 +7285,21 @@ namespace DynamORM /// Additional selectors to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); + + /// Add typed SQL DSL select items. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL where predicate. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL group by expressions. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL order by expressions. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); } /// Typed update query builder for mapped entities. /// Mapped entity type. @@ -7299,6 +7321,12 @@ namespace DynamORM /// Mapped object value. /// Builder instance. IDynamicTypedUpdateQueryBuilder Values(T value); + + /// Add typed SQL DSL where predicate. + IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL assignment. + IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory); } /// Dynamic update query builder interface. /// This interface it publicly available. Implementation should be hidden. @@ -7475,6 +7503,9 @@ namespace DynamORM /// Gets raw ON condition. public string OnRawCondition { get; private set; } + /// Gets typed SQL DSL ON specification. + public Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + /// Sets join alias. public TypedJoinBuilder As(string alias) { @@ -7560,6 +7591,7 @@ namespace DynamORM OnPredicate = predicate; OnRawCondition = null; + OnSqlPredicate = null; return this; } /// Sets raw ON clause (without the ON keyword). @@ -7570,6 +7602,18 @@ namespace DynamORM OnRawCondition = condition.Trim(); OnPredicate = null; + OnSqlPredicate = null; + return this; + } + /// Sets typed SQL DSL ON predicate. + public TypedJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnPredicate = null; + OnRawCondition = null; return this; } } @@ -8229,8 +8273,8 @@ namespace DynamORM /// Implementation of dynamic insert query builder. internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder { - private string _columns; - private string _values; + protected string _columns; + protected string _values; /// /// Initializes a new instance of the class. @@ -9338,11 +9382,11 @@ namespace DynamORM private int? _offset = null; private bool _distinct = false; - private string _select; + protected string _select; private string _from; protected string _join; - private string _groupby; - private string _orderby; + protected string _groupby; + protected string _orderby; #region IQueryWithHaving @@ -10678,6 +10722,16 @@ namespace DynamORM TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } + public IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } public new IDynamicTypedDeleteQueryBuilder Where(Func func) { base.Where(func); @@ -10703,6 +10757,14 @@ namespace DynamORM base.Where(conditions, schema); return this; } + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. @@ -10730,6 +10792,15 @@ namespace DynamORM base.Insert(value); return this; } + public IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); + string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName); + + _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 Values(Func fn, params Func[] func) { base.Values(fn, func); @@ -10745,11 +10816,54 @@ namespace DynamORM base.Insert(o); return this; } + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder { + private sealed class TypedSqlRenderContext : ITypedSqlRenderContext + { + private readonly DynamicTypedSelectQueryBuilder _builder; + + public TypedSqlRenderContext(DynamicTypedSelectQueryBuilder builder) + { + _builder = builder; + } + public string ResolveColumn(Type modelType, string memberName, string alias) + { + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + string mappedColumn = mapper.PropertyMap.TryGetValue(memberName) + ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() + ?? memberName; + + return string.IsNullOrEmpty(alias) + ? _builder.Database.DecorateName(mappedColumn) + : string.Format("{0}.{1}", alias, _builder.Database.DecorateName(mappedColumn)); + } + public string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return _builder.ParseConstant(value, _builder.Parameters, columnSchema); + } + public string DecorateName(string name) + { + return _builder.Database.DecorateName(name); + } + } private readonly DynamicTypeMap _mapper; public DynamicTypedSelectQueryBuilder(DynamicDatabase db) @@ -10846,7 +10960,12 @@ namespace DynamORM if (SupportNoLock && spec.UseNoLock) joinExpr += " WITH(NOLOCK)"; - if (!string.IsNullOrEmpty(spec.OnRawCondition)) + if (spec.OnSqlPredicate != null) + { + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + joinExpr += string.Format(" ON {0}", spec.OnSqlPredicate(new TypedTableContext(GetRootAliasOrTableName()), new TypedTableContext(rightAlias)).Render(context)); + } + else if (!string.IsNullOrEmpty(spec.OnRawCondition)) joinExpr += string.Format(" ON {0}", spec.OnRawCondition); AppendJoinClause(joinExpr); @@ -10894,6 +11013,18 @@ namespace DynamORM } return this; } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item); + + return this; + } public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -10911,6 +11042,18 @@ namespace DynamORM } return this; } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item); + + return this; + } public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -10928,6 +11071,18 @@ namespace DynamORM } return this; } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item); + + return this; + } public new IDynamicTypedSelectQueryBuilder Top(int? top) { base.Top(top); @@ -10962,6 +11117,32 @@ namespace DynamORM return this; } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = RenderSqlPredicate(predicate); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = RenderSqlPredicate(predicate); + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + + return this; + } public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) { base.Having(column); @@ -11000,6 +11181,16 @@ namespace DynamORM ((IDynamicSelectQueryBuilder)this).Select(x => parsed); } } + private void AddSelectSqlSelector(Func, TypedSqlSelectable> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlSelectable item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered); + } private void AddGroupBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); @@ -11018,6 +11209,16 @@ namespace DynamORM ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); } } + private void AddGroupBySqlSelector(Func, TypedSqlExpression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered); + } private void AddOrderBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); @@ -11032,6 +11233,21 @@ namespace DynamORM string parsed = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC"); ((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed); } + private void AddOrderBySqlSelector(Func, TypedSqlOrderExpression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlOrderExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered); + } + private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate) + { + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + return predicate(new TypedTableContext(GetRootAliasOrTableName())).Render(context); + } private string ParseTypedCondition(Expression expression) { expression = UnwrapConvert(expression); @@ -11415,6 +11631,24 @@ namespace DynamORM base.Values(value); return this; } + public IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + public IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); + string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName); + string assignment = string.Format("{0} = {1}", column, value); + _columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment); + return this; + } public new IDynamicTypedUpdateQueryBuilder Update(string column, object value) { base.Update(column, value); @@ -11465,11 +11699,19 @@ namespace DynamORM base.Where(conditions, schema); return this; } + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { - private string _columns; + protected string _columns; internal DynamicUpdateQueryBuilder(DynamicDatabase db) : base(db) @@ -11749,6 +11991,31 @@ namespace DynamORM /// Helper methods for typed modify builders. internal static class TypedModifyHelper { + private sealed class ModifyRenderContext : ITypedSqlRenderContext + { + private readonly Func, string> _resolveColumn; + private readonly Func _renderValue; + private readonly Func _decorateName; + + public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName) + { + _resolveColumn = resolveColumn; + _renderValue = renderValue; + _decorateName = decorateName; + } + public string ResolveColumn(Type modelType, string memberName, string alias) + { + return _resolveColumn(modelType, memberName, alias, _decorateName); + } + public string RenderValue(object value) + { + return _renderValue(value); + } + public string DecorateName(string name) + { + return _decorateName(name); + } + } public static string GetMappedColumn(Expression> selector) { if (selector == null) @@ -11765,6 +12032,30 @@ namespace DynamORM ApplyWhereInternal(typeof(T), addCondition, predicate.Body); } + public static string RenderPredicate( + Func, TypedSqlPredicate> predicate, + string alias, + Func renderValue, + Func decorateName) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName); + return predicate(new TypedTableContext(alias)).Render(context); + } + public static string RenderExpression( + Func, TypedSqlExpression> expression, + string alias, + Func renderValue, + Func decorateName) + { + if (expression == null) + throw new ArgumentNullException("expression"); + + ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName); + return expression(new TypedTableContext(alias)).Render(context); + } private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) { expression = UnwrapConvert(expression); @@ -11822,6 +12113,23 @@ namespace DynamORM .FirstOrDefault() ?? member.Member.Name; } + private static string ResolveColumn(Type modelType, string memberName, string alias, Func decorateName) + { + string mapped = GetMappedColumnByName(modelType, memberName); + return string.IsNullOrEmpty(alias) + ? decorateName(mapped) + : string.Format("{0}.{1}", alias, decorateName(mapped)); + } + private static string GetMappedColumnByName(Type modelType, string memberName) + { + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(memberName) + ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() + ?? memberName; + } private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression && @@ -15407,6 +15715,389 @@ namespace DynamORM } } } + namespace TypedSql + { + /// Render context used by typed SQL DSL nodes. + public interface ITypedSqlRenderContext + { + /// Resolve mapped column for given model member. + string ResolveColumn(Type modelType, string memberName, string alias); + + /// Render value as SQL parameter or literal fragment. + string RenderValue(object value); + + /// Decorate SQL identifier. + string DecorateName(string name); + } + /// Entry point for the typed SQL DSL. + public static class Sql + { + /// Create parameterized value expression. + public static TypedSqlExpression Val(T value) + { + return new TypedSqlValueExpression(value); + } + /// Create parameterized value expression. + public static TypedSqlExpression Val(object value) + { + return new TypedSqlValueExpression(value); + } + /// Create raw SQL expression. + public static TypedSqlExpression Raw(string sql) + { + return new TypedSqlRawExpression(sql); + } + /// Create generic function call. + public static TypedSqlExpression Func(string name, params TypedSqlExpression[] arguments) + { + return new TypedSqlFunctionExpression(name, arguments); + } + /// Create COUNT(*) expression. + public static TypedSqlExpression Count() + { + return Raw("COUNT(*)"); + } + /// Create COUNT(expr) expression. + public static TypedSqlExpression Count(TypedSqlExpression expression) + { + return Func("COUNT", expression); + } + /// Create COALESCE expression. + public static TypedSqlExpression Coalesce(params TypedSqlExpression[] expressions) + { + return Func("COALESCE", expressions); + } + /// Create CASE expression builder. + public static TypedSqlCaseBuilder Case() + { + return new TypedSqlCaseBuilder(); + } + } + /// Builder for CASE expressions. + /// Result type. + public sealed class TypedSqlCaseBuilder + { + private readonly IList> _cases = new List>(); + + /// Add WHEN ... THEN ... clause. + public TypedSqlCaseBuilder When(TypedSqlPredicate predicate, object value) + { + _cases.Add(new KeyValuePair( + predicate, + value as TypedSqlExpression ?? Sql.Val(value))); + return this; + } + /// Finalize CASE expression with ELSE clause. + public TypedSqlExpression Else(object value) + { + return new TypedSqlCaseExpression(_cases, value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Finalize CASE expression without ELSE clause. + public TypedSqlExpression End() + { + return new TypedSqlCaseExpression(_cases, null); + } + } + /// Base selectable SQL fragment for the typed DSL. + public abstract class TypedSqlSelectable + { + internal abstract string Render(ITypedSqlRenderContext context); + } + /// Base SQL expression for the typed DSL. + public abstract class TypedSqlExpression : TypedSqlSelectable + { + /// Alias this expression in SELECT clause. + public TypedSqlAliasedExpression As(string alias) + { + return new TypedSqlAliasedExpression(this, alias); + } + /// Order ascending. + public TypedSqlOrderExpression Asc() + { + return new TypedSqlOrderExpression(this, true); + } + /// Order descending. + public TypedSqlOrderExpression Desc() + { + return new TypedSqlOrderExpression(this, false); + } + /// Equality predicate. + public TypedSqlPredicate Eq(object value) + { + return new TypedSqlBinaryPredicate(this, "=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// Inequality predicate. + public TypedSqlPredicate NotEq(object value) + { + return new TypedSqlBinaryPredicate(this, "<>", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// Greater-than predicate. + public TypedSqlPredicate Gt(object value) + { + return new TypedSqlBinaryPredicate(this, ">", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// Greater-than-or-equal predicate. + public TypedSqlPredicate Gte(object value) + { + return new TypedSqlBinaryPredicate(this, ">=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// Less-than predicate. + public TypedSqlPredicate Lt(object value) + { + return new TypedSqlBinaryPredicate(this, "<", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// Less-than-or-equal predicate. + public TypedSqlPredicate Lte(object value) + { + return new TypedSqlBinaryPredicate(this, "<=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + /// IS NULL predicate. + public TypedSqlPredicate IsNull() + { + return new TypedSqlUnaryPredicate(this, "IS NULL"); + } + /// IS NOT NULL predicate. + public TypedSqlPredicate IsNotNull() + { + return new TypedSqlUnaryPredicate(this, "IS NOT NULL"); + } + } + /// Typed SQL expression. + public abstract class TypedSqlExpression : TypedSqlExpression + { + } + /// Typed SQL predicate expression. + public abstract class TypedSqlPredicate : TypedSqlExpression + { + /// Combine with AND. + public TypedSqlPredicate And(TypedSqlPredicate right) + { + return new TypedSqlCombinedPredicate(this, "AND", right); + } + /// Combine with OR. + public TypedSqlPredicate Or(TypedSqlPredicate right) + { + return new TypedSqlCombinedPredicate(this, "OR", right); + } + /// Negate predicate. + public TypedSqlPredicate Not() + { + return new TypedSqlNegatedPredicate(this); + } + } + /// Aliased SQL expression. + public sealed class TypedSqlAliasedExpression : TypedSqlSelectable + { + private readonly TypedSqlExpression _expression; + private readonly string _alias; + + internal TypedSqlAliasedExpression(TypedSqlExpression expression, string alias) + { + _expression = expression; + _alias = alias; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("{0} AS {1}", _expression.Render(context), context.DecorateName(_alias)); + } + } + /// Ordered SQL expression. + public sealed class TypedSqlOrderExpression : TypedSqlSelectable + { + private readonly TypedSqlExpression _expression; + private readonly bool _ascending; + + internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending) + { + _expression = expression; + _ascending = ascending; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + } + } + internal sealed class TypedSqlColumnExpression : TypedSqlExpression + { + private readonly Type _modelType; + private readonly string _memberName; + private readonly string _alias; + + internal TypedSqlColumnExpression(Type modelType, string memberName, string alias) + { + _modelType = modelType; + _memberName = memberName; + _alias = alias; + } + internal override string Render(ITypedSqlRenderContext context) + { + return context.ResolveColumn(_modelType, _memberName, _alias); + } + } + internal sealed class TypedSqlValueExpression : TypedSqlExpression + { + private readonly object _value; + + internal TypedSqlValueExpression(object value) + { + _value = value; + } + internal override string Render(ITypedSqlRenderContext context) + { + return context.RenderValue(_value); + } + } + internal sealed class TypedSqlRawExpression : TypedSqlExpression + { + private readonly string _sql; + + internal TypedSqlRawExpression(string sql) + { + _sql = sql; + } + internal override string Render(ITypedSqlRenderContext context) + { + return _sql; + } + } + internal sealed class TypedSqlFunctionExpression : TypedSqlExpression + { + private readonly string _name; + private readonly IList _arguments; + + internal TypedSqlFunctionExpression(string name, params TypedSqlExpression[] arguments) + { + _name = name; + _arguments = arguments ?? new TypedSqlExpression[0]; + } + internal override string Render(ITypedSqlRenderContext context) + { + List rendered = new List(); + foreach (TypedSqlExpression argument in _arguments) + rendered.Add(argument.Render(context)); + + return string.Format("{0}({1})", _name, string.Join(", ", rendered.ToArray())); + } + } + internal sealed class TypedSqlUnaryPredicate : TypedSqlPredicate + { + private readonly TypedSqlExpression _expression; + private readonly string _operator; + + internal TypedSqlUnaryPredicate(TypedSqlExpression expression, string op) + { + _expression = expression; + _operator = op; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1})", _expression.Render(context), _operator); + } + } + internal sealed class TypedSqlBinaryPredicate : TypedSqlPredicate + { + private readonly TypedSqlExpression _left; + private readonly string _operator; + private readonly TypedSqlExpression _right; + + internal TypedSqlBinaryPredicate(TypedSqlExpression left, string op, TypedSqlExpression right) + { + _left = left; + _operator = op; + _right = right; + } + internal override string Render(ITypedSqlRenderContext context) + { + string op = _operator; + if (_right is TypedSqlValueExpression && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase)) + op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator; + + return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context)); + } + } + internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate + { + private readonly TypedSqlPredicate _left; + private readonly string _operator; + private readonly TypedSqlPredicate _right; + + internal TypedSqlCombinedPredicate(TypedSqlPredicate left, string op, TypedSqlPredicate right) + { + _left = left; + _operator = op; + _right = right; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); + } + } + internal sealed class TypedSqlNegatedPredicate : TypedSqlPredicate + { + private readonly TypedSqlPredicate _predicate; + + internal TypedSqlNegatedPredicate(TypedSqlPredicate predicate) + { + _predicate = predicate; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("(NOT {0})", _predicate.Render(context)); + } + } + internal sealed class TypedSqlCaseExpression : TypedSqlExpression + { + private readonly IList> _cases; + private readonly TypedSqlExpression _elseExpression; + + internal TypedSqlCaseExpression(IList> cases, TypedSqlExpression elseExpression) + { + _cases = cases; + _elseExpression = elseExpression; + } + internal override string Render(ITypedSqlRenderContext context) + { + List items = new List(); + items.Add("CASE"); + + foreach (KeyValuePair item in _cases) + items.Add(string.Format("WHEN {0} THEN {1}", item.Key.Render(context), item.Value.Render(context))); + + if (_elseExpression != null) + items.Add(string.Format("ELSE {0}", _elseExpression.Render(context))); + + items.Add("END"); + return string.Join(" ", items.ToArray()); + } + } + /// Typed table context used by the typed SQL DSL. + /// Mapped entity type. + public sealed class TypedTableContext + { + internal TypedTableContext(string alias) + { + Alias = alias; + } + /// Gets table alias used by the current query. + public string Alias { get; private set; } + + /// Creates a mapped column expression. + public TypedSqlExpression Col(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + MemberExpression member = selector.Body as MemberExpression; + if (member == null && selector.Body is UnaryExpression) + member = ((UnaryExpression)selector.Body).Operand as MemberExpression; + + if (member == null) + throw new NotSupportedException(string.Format("Column selector must target a mapped property: {0}", selector)); + + return new TypedSqlColumnExpression(typeof(T), member.Member.Name, Alias); + } + } + } namespace Validation { /// Required attribute can be used to validate fields in objects using mapper class. diff --git a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs new file mode 100644 index 0000000..39695b6 --- /dev/null +++ b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs @@ -0,0 +1,119 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System.Linq; +using System.Text.RegularExpressions; +using DynamORM.Tests.Helpers; +using DynamORM.TypedSql; +using NUnit.Framework; + +namespace DynamORM.Tests.TypedSql +{ + [TestFixture] + public class TypedSqlDslTests : TestsBase + { + private static string NormalizeSql(string sql) + { + int index = 0; + return Regex.Replace(sql, @"\[\$[^\]]+\]", m => string.Format("[${0}]", index++)); + } + + [SetUp] + public void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset | + DynamicDatabaseOptions.SupportNoLock); + } + + [TearDown] + public void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + [Test] + public void TestSelectSqlWithFunctionsAndOrdering() + { + var cmd = Database.FromTyped("u") + .SelectSql( + u => Sql.Count().As("cnt"), + u => Sql.Coalesce(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value")) + .GroupBySql(u => Sql.Coalesce(u.Col(x => x.Code), Sql.Val("N/A"))) + .HavingSql(u => Sql.Count().Gt(1)) + .OrderBySql(u => Sql.Count().Desc()); + + Assert.AreEqual( + "SELECT COUNT(*) AS \"cnt\", COALESCE(u.\"user_code\", [$0]) AS \"code_value\" FROM \"sample_users\" AS u GROUP BY COALESCE(u.\"user_code\", [$1]) HAVING (COUNT(*) > [$2]) ORDER BY COUNT(*) DESC", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestSelectSqlWithCase() + { + var cmd = Database.FromTyped("u") + .SelectSql(u => Sql.Case() + .When(u.Col(x => x.Code).Eq("A"), "Alpha") + .When(u.Col(x => x.Code).Eq("B"), "Beta") + .Else("Other") + .As("category")); + + Assert.AreEqual( + "SELECT CASE WHEN (u.\"user_code\" = [$0]) THEN [$1] WHEN (u.\"user_code\" = [$2]) THEN [$3] ELSE [$4] END AS \"category\" FROM \"sample_users\" AS u", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestJoinOnSql() + { + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id)).And(r.Col(a => a.Code).IsNotNull()))) + .SelectSql(u => u.Col(x => x.Id)); + + Assert.AreEqual("SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON ((u.\"id_user\" = x.\"id_user\") AND (x.\"user_code\" IS NOT NULL))", + cmd.CommandText()); + } + + [Test] + public void TestUpdateSetSqlAndWhereSql() + { + var cmd = Database.UpdateTyped() + .SetSql(u => u.Code, u => Sql.Coalesce(Sql.Val("900"), u.Col(x => x.Code))) + .WhereSql(u => u.Col(x => x.Id).Eq(1).And(u.Col(x => x.Code).IsNotNull())); + + Assert.AreEqual( + "UPDATE \"sample_users\" SET \"code\" = COALESCE([$0], \"code\") WHERE ((\"id\" = [$1]) AND (\"code\" IS NOT NULL))", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestInsertSqlSupportsCase() + { + var cmd = Database.InsertTyped() + .InsertSql(u => u.Code, u => Sql.Coalesce(Sql.Val("901"), Sql.Val("fallback"))) + .InsertSql(u => u.First, u => Sql.Case().When(Sql.Val(1).Eq(1), "Typed").Else("Other")); + + Assert.AreEqual( + "INSERT INTO \"sample_users\" (\"code\", \"first\") VALUES (COALESCE([$0], [$1]), CASE WHEN ([$2] = [$3]) THEN [$4] ELSE [$5] END)", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestDeleteWhereSql() + { + var cmd = Database.DeleteTyped() + .WhereSql(u => u.Col(x => x.Id).Eq(2).And(u.Col(x => x.Code).NotEq("X"))); + + Assert.AreEqual( + "DELETE FROM \"sample_users\" WHERE ((\"id\" = [$0]) AND (\"code\" <> [$1]))", + NormalizeSql(cmd.CommandText())); + } + } +} diff --git a/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs index 3ce0b86..d643ea3 100644 --- a/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedDeleteQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.TypedSql; namespace DynamORM.Builders { @@ -17,5 +18,8 @@ namespace DynamORM.Builders /// Predicate to parse. /// Builder instance. IDynamicTypedDeleteQueryBuilder Where(Expression> predicate); + + /// Add typed SQL DSL where predicate. + IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); } } diff --git a/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs index 2a1f8d4..869eddd 100644 --- a/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.TypedSql; namespace DynamORM.Builders { @@ -24,5 +25,8 @@ namespace DynamORM.Builders /// Mapped object value. /// Builder instance. IDynamicTypedInsertQueryBuilder Insert(T value); + + /// Add typed SQL DSL insert assignment. + IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory); } } diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index 4a637a5..f165c6c 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -28,6 +28,7 @@ using System; using System.Linq.Expressions; +using DynamORM.TypedSql; namespace DynamORM.Builders { @@ -89,5 +90,20 @@ namespace DynamORM.Builders /// Additional selectors to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); + + /// Add typed SQL DSL select items. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL where predicate. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL group by expressions. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL order by expressions. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); } } diff --git a/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs index 6285f12..d9842bc 100644 --- a/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.TypedSql; namespace DynamORM.Builders { @@ -29,5 +30,11 @@ namespace DynamORM.Builders /// Mapped object value. /// Builder instance. IDynamicTypedUpdateQueryBuilder Values(T value); + + /// Add typed SQL DSL where predicate. + IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL assignment. + IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory); } } diff --git a/DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs index 35d78bf..45aa71d 100644 --- a/DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicInsertQueryBuilder.cs @@ -40,8 +40,8 @@ namespace DynamORM.Builders.Implementation /// Implementation of dynamic insert query builder. internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder { - private string _columns; - private string _values; + protected string _columns; + protected string _values; /// /// Initializes a new instance of the class. @@ -221,4 +221,4 @@ namespace DynamORM.Builders.Implementation #endregion IExtendedDisposable } -} \ No newline at end of file +} diff --git a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs index f2bae87..e95dfb4 100644 --- a/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicSelectQueryBuilder.cs @@ -48,11 +48,11 @@ namespace DynamORM.Builders.Implementation private int? _offset = null; private bool _distinct = false; - private string _select; - private string _from; + protected string _select; + private string _from; protected string _join; - private string _groupby; - private string _orderby; + protected string _groupby; + protected string _orderby; #region IQueryWithHaving diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs index f475f95..33403e4 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -7,6 +7,7 @@ using System; using System.Linq.Expressions; using DynamORM.Builders.Extensions; +using DynamORM.TypedSql; namespace DynamORM.Builders.Implementation { @@ -34,6 +35,17 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + public new IDynamicTypedDeleteQueryBuilder Where(Func func) { base.Where(func); @@ -63,5 +75,14 @@ namespace DynamORM.Builders.Implementation base.Where(conditions, schema); return this; } + + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs index 2ae6ee1..4c087ce 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -7,6 +7,7 @@ using System; using System.Linq.Expressions; using DynamORM.Builders.Extensions; +using DynamORM.TypedSql; namespace DynamORM.Builders.Implementation { @@ -40,6 +41,16 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); + string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName); + + _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 Values(Func fn, params Func[] func) { base.Values(fn, func); @@ -57,5 +68,14 @@ namespace DynamORM.Builders.Implementation base.Insert(o); return this; } + + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index af340e0..4f681af 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -34,6 +34,7 @@ using System.Linq.Expressions; using System.Reflection; using DynamORM.Helpers; using DynamORM.Mapper; +using DynamORM.TypedSql; namespace DynamORM.Builders.Implementation { @@ -41,6 +42,45 @@ namespace DynamORM.Builders.Implementation /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder { + private sealed class TypedSqlRenderContext : ITypedSqlRenderContext + { + private readonly DynamicTypedSelectQueryBuilder _builder; + + public TypedSqlRenderContext(DynamicTypedSelectQueryBuilder builder) + { + _builder = builder; + } + + public string ResolveColumn(Type modelType, string memberName, string alias) + { + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + string mappedColumn = mapper.PropertyMap.TryGetValue(memberName) + ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() + ?? memberName; + + return string.IsNullOrEmpty(alias) + ? _builder.Database.DecorateName(mappedColumn) + : string.Format("{0}.{1}", alias, _builder.Database.DecorateName(mappedColumn)); + } + + public string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return _builder.ParseConstant(value, _builder.Parameters, columnSchema); + } + + public string DecorateName(string name) + { + return _builder.Database.DecorateName(name); + } + } + private readonly DynamicTypeMap _mapper; public DynamicTypedSelectQueryBuilder(DynamicDatabase db) @@ -141,7 +181,12 @@ namespace DynamORM.Builders.Implementation if (SupportNoLock && spec.UseNoLock) joinExpr += " WITH(NOLOCK)"; - if (!string.IsNullOrEmpty(spec.OnRawCondition)) + if (spec.OnSqlPredicate != null) + { + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + joinExpr += string.Format(" ON {0}", spec.OnSqlPredicate(new TypedTableContext(GetRootAliasOrTableName()), new TypedTableContext(rightAlias)).Render(context)); + } + else if (!string.IsNullOrEmpty(spec.OnRawCondition)) joinExpr += string.Format(" ON {0}", spec.OnRawCondition); AppendJoinClause(joinExpr); @@ -197,6 +242,19 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item); + + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -216,6 +274,19 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item); + + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -235,6 +306,19 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector); + if (selectors != null) + foreach (Func, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item); + + return this; + } + public new IDynamicTypedSelectQueryBuilder Top(int? top) { base.Top(top); @@ -274,6 +358,34 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = RenderSqlPredicate(predicate); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + string condition = RenderSqlPredicate(predicate); + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) { base.Having(column); @@ -317,6 +429,17 @@ namespace DynamORM.Builders.Implementation } } + private void AddSelectSqlSelector(Func, TypedSqlSelectable> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlSelectable item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered); + } + private void AddGroupBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); @@ -336,6 +459,17 @@ namespace DynamORM.Builders.Implementation } } + private void AddGroupBySqlSelector(Func, TypedSqlExpression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered); + } + private void AddOrderBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); @@ -352,6 +486,23 @@ namespace DynamORM.Builders.Implementation ((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed); } + private void AddOrderBySqlSelector(Func, TypedSqlOrderExpression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + TypedSqlOrderExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); + string rendered = item.Render(context); + _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered); + } + + private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate) + { + TypedSqlRenderContext context = new TypedSqlRenderContext(this); + return predicate(new TypedTableContext(GetRootAliasOrTableName())).Render(context); + } + private string ParseTypedCondition(Expression expression) { expression = UnwrapConvert(expression); diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs index 6b6720d..2fd2752 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -7,6 +7,7 @@ using System; using System.Linq.Expressions; using DynamORM.Builders.Extensions; +using DynamORM.TypedSql; namespace DynamORM.Builders.Implementation { @@ -46,6 +47,26 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName); + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + + return this; + } + + public IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); + string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName); + string assignment = string.Format("{0} = {1}", column, value); + _columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment); + return this; + } + public new IDynamicTypedUpdateQueryBuilder Update(string column, object value) { base.Update(column, value); @@ -105,5 +126,14 @@ namespace DynamORM.Builders.Implementation base.Where(conditions, schema); return this; } + + private string RenderValue(object value) + { + if (value == null) + return "NULL"; + + DynamicSchemaColumn? columnSchema = null; + return ParseConstant(value, Parameters, columnSchema); + } } } diff --git a/DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs index 030382c..f329755 100644 --- a/DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicUpdateQueryBuilder.cs @@ -41,7 +41,7 @@ namespace DynamORM.Builders.Implementation /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { - private string _columns; + protected string _columns; internal DynamicUpdateQueryBuilder(DynamicDatabase db) : base(db) @@ -339,4 +339,4 @@ namespace DynamORM.Builders.Implementation #endregion IExtendedDisposable } -} \ No newline at end of file +} diff --git a/DynamORM/Builders/Implementation/TypedModifyHelper.cs b/DynamORM/Builders/Implementation/TypedModifyHelper.cs index 8625026..a75309a 100644 --- a/DynamORM/Builders/Implementation/TypedModifyHelper.cs +++ b/DynamORM/Builders/Implementation/TypedModifyHelper.cs @@ -5,15 +5,46 @@ */ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using DynamORM.Mapper; +using DynamORM.TypedSql; namespace DynamORM.Builders.Implementation { /// Helper methods for typed modify builders. internal static class TypedModifyHelper { + private sealed class ModifyRenderContext : ITypedSqlRenderContext + { + private readonly Func, string> _resolveColumn; + private readonly Func _renderValue; + private readonly Func _decorateName; + + public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName) + { + _resolveColumn = resolveColumn; + _renderValue = renderValue; + _decorateName = decorateName; + } + + public string ResolveColumn(Type modelType, string memberName, string alias) + { + return _resolveColumn(modelType, memberName, alias, _decorateName); + } + + public string RenderValue(object value) + { + return _renderValue(value); + } + + public string DecorateName(string name) + { + return _decorateName(name); + } + } + public static string GetMappedColumn(Expression> selector) { if (selector == null) @@ -32,6 +63,32 @@ namespace DynamORM.Builders.Implementation ApplyWhereInternal(typeof(T), addCondition, predicate.Body); } + public static string RenderPredicate( + Func, TypedSqlPredicate> predicate, + string alias, + Func renderValue, + Func decorateName) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName); + return predicate(new TypedTableContext(alias)).Render(context); + } + + public static string RenderExpression( + Func, TypedSqlExpression> expression, + string alias, + Func renderValue, + Func decorateName) + { + if (expression == null) + throw new ArgumentNullException("expression"); + + ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName); + return expression(new TypedTableContext(alias)).Render(context); + } + private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) { expression = UnwrapConvert(expression); @@ -93,6 +150,25 @@ namespace DynamORM.Builders.Implementation ?? member.Member.Name; } + private static string ResolveColumn(Type modelType, string memberName, string alias, Func decorateName) + { + string mapped = GetMappedColumnByName(modelType, memberName); + return string.IsNullOrEmpty(alias) + ? decorateName(mapped) + : string.Format("{0}.{1}", alias, decorateName(mapped)); + } + + private static string GetMappedColumnByName(Type modelType, string memberName) + { + DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); + if (mapper == null) + throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); + + return mapper.PropertyMap.TryGetValue(memberName) + ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() + ?? memberName; + } + private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression && diff --git a/DynamORM/Builders/TypedJoinBuilder.cs b/DynamORM/Builders/TypedJoinBuilder.cs index ac0440b..a0ae403 100644 --- a/DynamORM/Builders/TypedJoinBuilder.cs +++ b/DynamORM/Builders/TypedJoinBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; +using DynamORM.TypedSql; namespace DynamORM.Builders { @@ -37,6 +38,9 @@ namespace DynamORM.Builders /// Gets raw ON condition. public string OnRawCondition { get; private set; } + /// Gets typed SQL DSL ON specification. + public Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + /// Sets join alias. public TypedJoinBuilder As(string alias) { @@ -133,6 +137,7 @@ namespace DynamORM.Builders OnPredicate = predicate; OnRawCondition = null; + OnSqlPredicate = null; return this; } @@ -144,6 +149,19 @@ namespace DynamORM.Builders OnRawCondition = condition.Trim(); OnPredicate = null; + OnSqlPredicate = null; + return this; + } + + /// Sets typed SQL DSL ON predicate. + public TypedJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnPredicate = null; + OnRawCondition = null; return this; } } diff --git a/DynamORM/TypedSql/ITypedSqlRenderContext.cs b/DynamORM/TypedSql/ITypedSqlRenderContext.cs new file mode 100644 index 0000000..58ed69d --- /dev/null +++ b/DynamORM/TypedSql/ITypedSqlRenderContext.cs @@ -0,0 +1,23 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; + +namespace DynamORM.TypedSql +{ + /// Render context used by typed SQL DSL nodes. + public interface ITypedSqlRenderContext + { + /// Resolve mapped column for given model member. + string ResolveColumn(Type modelType, string memberName, string alias); + + /// Render value as SQL parameter or literal fragment. + string RenderValue(object value); + + /// Decorate SQL identifier. + string DecorateName(string name); + } +} diff --git a/DynamORM/TypedSql/Sql.cs b/DynamORM/TypedSql/Sql.cs new file mode 100644 index 0000000..91f46d5 --- /dev/null +++ b/DynamORM/TypedSql/Sql.cs @@ -0,0 +1,91 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Collections.Generic; + +namespace DynamORM.TypedSql +{ + /// Entry point for the typed SQL DSL. + public static class Sql + { + /// Create parameterized value expression. + public static TypedSqlExpression Val(T value) + { + return new TypedSqlValueExpression(value); + } + + /// Create parameterized value expression. + public static TypedSqlExpression Val(object value) + { + return new TypedSqlValueExpression(value); + } + + /// Create raw SQL expression. + public static TypedSqlExpression Raw(string sql) + { + return new TypedSqlRawExpression(sql); + } + + /// Create generic function call. + public static TypedSqlExpression Func(string name, params TypedSqlExpression[] arguments) + { + return new TypedSqlFunctionExpression(name, arguments); + } + + /// Create COUNT(*) expression. + public static TypedSqlExpression Count() + { + return Raw("COUNT(*)"); + } + + /// Create COUNT(expr) expression. + public static TypedSqlExpression Count(TypedSqlExpression expression) + { + return Func("COUNT", expression); + } + + /// Create COALESCE expression. + public static TypedSqlExpression Coalesce(params TypedSqlExpression[] expressions) + { + return Func("COALESCE", expressions); + } + + /// Create CASE expression builder. + public static TypedSqlCaseBuilder Case() + { + return new TypedSqlCaseBuilder(); + } + } + + /// Builder for CASE expressions. + /// Result type. + public sealed class TypedSqlCaseBuilder + { + private readonly IList> _cases = new List>(); + + /// Add WHEN ... THEN ... clause. + public TypedSqlCaseBuilder When(TypedSqlPredicate predicate, object value) + { + _cases.Add(new KeyValuePair( + predicate, + value as TypedSqlExpression ?? Sql.Val(value))); + return this; + } + + /// Finalize CASE expression with ELSE clause. + public TypedSqlExpression Else(object value) + { + return new TypedSqlCaseExpression(_cases, value as TypedSqlExpression ?? Sql.Val(value)); + } + + /// Finalize CASE expression without ELSE clause. + public TypedSqlExpression End() + { + return new TypedSqlCaseExpression(_cases, null); + } + } +} diff --git a/DynamORM/TypedSql/TypedSqlExpression.cs b/DynamORM/TypedSql/TypedSqlExpression.cs new file mode 100644 index 0000000..7676013 --- /dev/null +++ b/DynamORM/TypedSql/TypedSqlExpression.cs @@ -0,0 +1,321 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Collections.Generic; + +namespace DynamORM.TypedSql +{ + /// Base selectable SQL fragment for the typed DSL. + public abstract class TypedSqlSelectable + { + internal abstract string Render(ITypedSqlRenderContext context); + } + + /// Base SQL expression for the typed DSL. + public abstract class TypedSqlExpression : TypedSqlSelectable + { + /// Alias this expression in SELECT clause. + public TypedSqlAliasedExpression As(string alias) + { + return new TypedSqlAliasedExpression(this, alias); + } + + /// Order ascending. + public TypedSqlOrderExpression Asc() + { + return new TypedSqlOrderExpression(this, true); + } + + /// Order descending. + public TypedSqlOrderExpression Desc() + { + return new TypedSqlOrderExpression(this, false); + } + + /// Equality predicate. + public TypedSqlPredicate Eq(object value) + { + return new TypedSqlBinaryPredicate(this, "=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// Inequality predicate. + public TypedSqlPredicate NotEq(object value) + { + return new TypedSqlBinaryPredicate(this, "<>", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// Greater-than predicate. + public TypedSqlPredicate Gt(object value) + { + return new TypedSqlBinaryPredicate(this, ">", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// Greater-than-or-equal predicate. + public TypedSqlPredicate Gte(object value) + { + return new TypedSqlBinaryPredicate(this, ">=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// Less-than predicate. + public TypedSqlPredicate Lt(object value) + { + return new TypedSqlBinaryPredicate(this, "<", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// Less-than-or-equal predicate. + public TypedSqlPredicate Lte(object value) + { + return new TypedSqlBinaryPredicate(this, "<=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); + } + + /// IS NULL predicate. + public TypedSqlPredicate IsNull() + { + return new TypedSqlUnaryPredicate(this, "IS NULL"); + } + + /// IS NOT NULL predicate. + public TypedSqlPredicate IsNotNull() + { + return new TypedSqlUnaryPredicate(this, "IS NOT NULL"); + } + } + + /// Typed SQL expression. + public abstract class TypedSqlExpression : TypedSqlExpression + { + } + + /// Typed SQL predicate expression. + public abstract class TypedSqlPredicate : TypedSqlExpression + { + /// Combine with AND. + public TypedSqlPredicate And(TypedSqlPredicate right) + { + return new TypedSqlCombinedPredicate(this, "AND", right); + } + + /// Combine with OR. + public TypedSqlPredicate Or(TypedSqlPredicate right) + { + return new TypedSqlCombinedPredicate(this, "OR", right); + } + + /// Negate predicate. + public TypedSqlPredicate Not() + { + return new TypedSqlNegatedPredicate(this); + } + } + + /// Aliased SQL expression. + public sealed class TypedSqlAliasedExpression : TypedSqlSelectable + { + private readonly TypedSqlExpression _expression; + private readonly string _alias; + + internal TypedSqlAliasedExpression(TypedSqlExpression expression, string alias) + { + _expression = expression; + _alias = alias; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("{0} AS {1}", _expression.Render(context), context.DecorateName(_alias)); + } + } + + /// Ordered SQL expression. + public sealed class TypedSqlOrderExpression : TypedSqlSelectable + { + private readonly TypedSqlExpression _expression; + private readonly bool _ascending; + + internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending) + { + _expression = expression; + _ascending = ascending; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + } + } + + internal sealed class TypedSqlColumnExpression : TypedSqlExpression + { + private readonly Type _modelType; + private readonly string _memberName; + private readonly string _alias; + + internal TypedSqlColumnExpression(Type modelType, string memberName, string alias) + { + _modelType = modelType; + _memberName = memberName; + _alias = alias; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return context.ResolveColumn(_modelType, _memberName, _alias); + } + } + + internal sealed class TypedSqlValueExpression : TypedSqlExpression + { + private readonly object _value; + + internal TypedSqlValueExpression(object value) + { + _value = value; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return context.RenderValue(_value); + } + } + + internal sealed class TypedSqlRawExpression : TypedSqlExpression + { + private readonly string _sql; + + internal TypedSqlRawExpression(string sql) + { + _sql = sql; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return _sql; + } + } + + internal sealed class TypedSqlFunctionExpression : TypedSqlExpression + { + private readonly string _name; + private readonly IList _arguments; + + internal TypedSqlFunctionExpression(string name, params TypedSqlExpression[] arguments) + { + _name = name; + _arguments = arguments ?? new TypedSqlExpression[0]; + } + + internal override string Render(ITypedSqlRenderContext context) + { + List rendered = new List(); + foreach (TypedSqlExpression argument in _arguments) + rendered.Add(argument.Render(context)); + + return string.Format("{0}({1})", _name, string.Join(", ", rendered.ToArray())); + } + } + + internal sealed class TypedSqlUnaryPredicate : TypedSqlPredicate + { + private readonly TypedSqlExpression _expression; + private readonly string _operator; + + internal TypedSqlUnaryPredicate(TypedSqlExpression expression, string op) + { + _expression = expression; + _operator = op; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1})", _expression.Render(context), _operator); + } + } + + internal sealed class TypedSqlBinaryPredicate : TypedSqlPredicate + { + private readonly TypedSqlExpression _left; + private readonly string _operator; + private readonly TypedSqlExpression _right; + + internal TypedSqlBinaryPredicate(TypedSqlExpression left, string op, TypedSqlExpression right) + { + _left = left; + _operator = op; + _right = right; + } + + internal override string Render(ITypedSqlRenderContext context) + { + string op = _operator; + if (_right is TypedSqlValueExpression && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase)) + op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator; + + return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context)); + } + } + + internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate + { + private readonly TypedSqlPredicate _left; + private readonly string _operator; + private readonly TypedSqlPredicate _right; + + internal TypedSqlCombinedPredicate(TypedSqlPredicate left, string op, TypedSqlPredicate right) + { + _left = left; + _operator = op; + _right = right; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); + } + } + + internal sealed class TypedSqlNegatedPredicate : TypedSqlPredicate + { + private readonly TypedSqlPredicate _predicate; + + internal TypedSqlNegatedPredicate(TypedSqlPredicate predicate) + { + _predicate = predicate; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("(NOT {0})", _predicate.Render(context)); + } + } + + internal sealed class TypedSqlCaseExpression : TypedSqlExpression + { + private readonly IList> _cases; + private readonly TypedSqlExpression _elseExpression; + + internal TypedSqlCaseExpression(IList> cases, TypedSqlExpression elseExpression) + { + _cases = cases; + _elseExpression = elseExpression; + } + + internal override string Render(ITypedSqlRenderContext context) + { + List items = new List(); + items.Add("CASE"); + + foreach (KeyValuePair item in _cases) + items.Add(string.Format("WHEN {0} THEN {1}", item.Key.Render(context), item.Value.Render(context))); + + if (_elseExpression != null) + items.Add(string.Format("ELSE {0}", _elseExpression.Render(context))); + + items.Add("END"); + return string.Join(" ", items.ToArray()); + } + } +} diff --git a/DynamORM/TypedSql/TypedTableContext.cs b/DynamORM/TypedSql/TypedTableContext.cs new file mode 100644 index 0000000..39adad9 --- /dev/null +++ b/DynamORM/TypedSql/TypedTableContext.cs @@ -0,0 +1,40 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Linq.Expressions; + +namespace DynamORM.TypedSql +{ + /// Typed table context used by the typed SQL DSL. + /// Mapped entity type. + public sealed class TypedTableContext + { + internal TypedTableContext(string alias) + { + Alias = alias; + } + + /// Gets table alias used by the current query. + public string Alias { get; private set; } + + /// Creates a mapped column expression. + public TypedSqlExpression Col(Expression> selector) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + MemberExpression member = selector.Body as MemberExpression; + if (member == null && selector.Body is UnaryExpression) + member = ((UnaryExpression)selector.Body).Operand as MemberExpression; + + if (member == null) + throw new NotSupportedException(string.Format("Column selector must target a mapped property: {0}", selector)); + + return new TypedSqlColumnExpression(typeof(T), member.Member.Name, Alias); + } + } +} From 97ab4c1e15a7781313dc5f5aebd7121bcbbbe0c3 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 08:34:16 +0100 Subject: [PATCH 11/20] Expand typed SQL DSL with predicates, subqueries and projections --- AmalgamationTool/DynamORM.Amalgamation.cs | 271 +++++++++++++++++- DynamORM.Tests/TypedSql/TypedSqlDslTests.cs | 129 +++++++++ .../IDynamicTypedInsertQueryBuilder.cs | 3 + .../IDynamicTypedUpdateQueryBuilder.cs | 3 + .../DynamicTypedDeleteQueryBuilder.cs | 12 +- .../DynamicTypedInsertQueryBuilder.cs | 33 ++- .../DynamicTypedSelectQueryBuilder.cs | 12 + .../DynamicTypedUpdateQueryBuilder.cs | 35 ++- .../Implementation/TypedModifyHelper.cs | 22 +- DynamORM/TypedSql/ITypedSqlRenderContext.cs | 3 + DynamORM/TypedSql/Sql.cs | 79 +++++ DynamORM/TypedSql/TypedSqlExpression.cs | 114 +++++++- 12 files changed, 692 insertions(+), 24 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 212eb91..1109c46 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7226,6 +7226,9 @@ namespace DynamORM /// Add typed SQL DSL insert assignment. IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory); + + /// Add typed SQL DSL insert assignments from object projection. + IDynamicTypedInsertQueryBuilder InsertSql(Func, object> values); } /// Typed select query builder for mapped entities. /// Mapped entity type. @@ -7327,6 +7330,9 @@ namespace DynamORM /// Add typed SQL DSL assignment. IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory); + + /// Add typed SQL DSL assignments from object projection. + IDynamicTypedUpdateQueryBuilder SetSql(Func, object> values); } /// Dynamic update query builder interface. /// This interface it publicly available. Implementation should be hidden. @@ -10724,7 +10730,7 @@ namespace DynamORM } public IDynamicTypedDeleteQueryBuilder WhereSql(Func, 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. @@ -10795,12 +10809,30 @@ namespace DynamORM public IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, 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 InsertSql(Func, object> values) + { + if (values == null) + throw new ArgumentNullException("values"); + + object data = values(new TypedTableContext(null)); + foreach (KeyValuePair item in data.ToDictionary()) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); + string value = (item.Value as TypedSqlExpression) != null + ? TypedModifyHelper.RenderExpression(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 Values(Func fn, params Func[] 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. @@ -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 WhereSql(Func, 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 SetSql(Expression> selector, Func, 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 SetSql(Func, object> values) + { + if (values == null) + throw new ArgumentNullException("values"); + + object data = values(new TypedTableContext(null)); + foreach (KeyValuePair item in data.ToDictionary()) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); + string value = (item.Value as TypedSqlExpression) != null + ? TypedModifyHelper.RenderExpression(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 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere @@ -11996,12 +12073,14 @@ namespace DynamORM private readonly Func, string> _resolveColumn; private readonly Func _renderValue; private readonly Func _decorateName; + private readonly Func _renderSubQuery; - public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName) + public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName, Func 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(Expression> selector) { @@ -12036,24 +12119,26 @@ namespace DynamORM Func, TypedSqlPredicate> predicate, string alias, Func renderValue, - Func decorateName) + Func decorateName, + Func 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(alias)).Render(context); } public static string RenderExpression( Func, TypedSqlExpression> expression, string alias, Func renderValue, - Func decorateName) + Func decorateName, + Func 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(alias)).Render(context); } private static void ApplyWhereInternal(Type modelType, Action 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 /// Decorate SQL identifier. string DecorateName(string name); + + /// Render subquery SQL and merge any parameters into current context. + string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query); } /// Entry point for the typed SQL DSL. public static class Sql @@ -15767,6 +15855,71 @@ namespace DynamORM { return Func("COALESCE", expressions); } + /// Create SUM expression. + public static TypedSqlExpression Sum(TypedSqlExpression expression) + { + return Func("SUM", expression); + } + /// Create AVG expression. + public static TypedSqlExpression Avg(TypedSqlExpression expression) + { + return Func("AVG", expression); + } + /// Create MIN expression. + public static TypedSqlExpression Min(TypedSqlExpression expression) + { + return Func("MIN", expression); + } + /// Create MAX expression. + public static TypedSqlExpression Max(TypedSqlExpression expression) + { + return Func("MAX", expression); + } + /// Create ABS expression. + public static TypedSqlExpression Abs(TypedSqlExpression expression) + { + return Func("ABS", expression); + } + /// Create UPPER expression. + public static TypedSqlExpression Upper(TypedSqlExpression expression) + { + return Func("UPPER", expression); + } + /// Create LOWER expression. + public static TypedSqlExpression Lower(TypedSqlExpression expression) + { + return Func("LOWER", expression); + } + /// Create TRIM expression. + public static TypedSqlExpression Trim(TypedSqlExpression expression) + { + return Func("TRIM", expression); + } + /// Create LENGTH expression. + public static TypedSqlExpression Length(TypedSqlExpression expression) + { + return Func("LENGTH", expression); + } + /// Create NULLIF expression. + public static TypedSqlExpression NullIf(TypedSqlExpression left, TypedSqlExpression right) + { + return Func("NULLIF", left, right); + } + /// Create CURRENT_TIMESTAMP expression. + public static TypedSqlExpression CurrentTimestamp() + { + return Raw("CURRENT_TIMESTAMP"); + } + /// Create scalar subquery expression. + public static TypedSqlExpression SubQuery(IDynamicSelectQueryBuilder query) + { + return new TypedSqlSubQueryExpression(query); + } + /// Create EXISTS predicate. + public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) + { + return new TypedSqlExistsPredicate(query); + } /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { @@ -15861,6 +16014,26 @@ namespace DynamORM { return new TypedSqlUnaryPredicate(this, "IS NOT NULL"); } + /// LIKE predicate. + public TypedSqlPredicate Like(string pattern) + { + return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern)); + } + /// IN predicate. + public TypedSqlPredicate In(params object[] values) + { + return new TypedSqlInPredicate(this, values); + } + /// IN predicate. + public TypedSqlPredicate In(IEnumerable values) + { + return new TypedSqlInPredicate(this, values); + } + /// BETWEEN predicate. + 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)); + } } /// Typed SQL expression. public abstract class TypedSqlExpression : TypedSqlExpression @@ -15934,7 +16107,7 @@ namespace DynamORM return context.ResolveColumn(_modelType, _memberName, _alias); } } - internal sealed class TypedSqlValueExpression : TypedSqlExpression + internal sealed class TypedSqlValueExpression : TypedSqlExpression, 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 : TypedSqlExpression { @@ -16009,12 +16186,56 @@ namespace DynamORM internal override string Render(ITypedSqlRenderContext context) { string op = _operator; - if (_right is TypedSqlValueExpression && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase)) + TypedSqlValueExpression objRight = _right as TypedSqlValueExpression; + 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 rendered = new List(); + 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 : TypedSqlExpression + { + 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)); + } + } /// Typed table context used by the typed SQL DSL. /// Mapped entity type. public sealed class TypedTableContext diff --git a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs index 39695b6..7b2e49c 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs @@ -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("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("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("u") + .SelectSql( + u => Sql.Sum(u.Col(x => x.Id)).As("sum_id"), + u => Sql.Avg(u.Col(x => x.Id)).As("avg_id"), + u => Sql.Min(u.Col(x => x.Id)).As("min_id"), + u => Sql.Max(u.Col(x => x.Id)).As("max_id"), + u => Sql.Abs(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(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("u") + .SelectSql(u => Sql.Func("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("u") + .SelectSql(u => Sql.SubQuery(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("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() + .InsertSql(u => new + { + Code = Sql.SubQuery(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() + .SetSql(u => new + { + Code = Sql.SubQuery(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())); + } } } diff --git a/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs index 869eddd..87a6f1c 100644 --- a/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedInsertQueryBuilder.cs @@ -28,5 +28,8 @@ namespace DynamORM.Builders /// Add typed SQL DSL insert assignment. IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory); + + /// Add typed SQL DSL insert assignments from object projection. + IDynamicTypedInsertQueryBuilder InsertSql(Func, object> values); } } diff --git a/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs index d9842bc..5d3aa9e 100644 --- a/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedUpdateQueryBuilder.cs @@ -36,5 +36,8 @@ namespace DynamORM.Builders /// Add typed SQL DSL assignment. IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory); + + /// Add typed SQL DSL assignments from object projection. + IDynamicTypedUpdateQueryBuilder SetSql(Func, object> values); } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs index 33403e4..9b94847 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedDeleteQueryBuilder.cs @@ -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 WhereSql(Func, 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs index 4c087ce..7436587 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedInsertQueryBuilder.cs @@ -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 InsertSql(Expression> selector, Func, 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 InsertSql(Func, object> values) + { + if (values == null) + throw new ArgumentNullException("values"); + + object data = values(new TypedTableContext(null)); + foreach (KeyValuePair item in data.ToDictionary()) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); + string value = (item.Value as TypedSqlExpression) != null + ? TypedModifyHelper.RenderExpression(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 Values(Func fn, params Func[] 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index 4f681af..c86b9d4 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -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; diff --git a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs index 2fd2752..d636544 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedUpdateQueryBuilder.cs @@ -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 WhereSql(Func, 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 SetSql(Expression> selector, Func, 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 SetSql(Func, object> values) + { + if (values == null) + throw new ArgumentNullException("values"); + + object data = values(new TypedTableContext(null)); + foreach (KeyValuePair item in data.ToDictionary()) + { + string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); + string value = (item.Value as TypedSqlExpression) != null + ? TypedModifyHelper.RenderExpression(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 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 item in query.Parameters) + if (!Parameters.ContainsKey(item.Key)) + Parameters.Add(item.Key, item.Value); + + return query.CommandText(); + } } } diff --git a/DynamORM/Builders/Implementation/TypedModifyHelper.cs b/DynamORM/Builders/Implementation/TypedModifyHelper.cs index a75309a..421ae80 100644 --- a/DynamORM/Builders/Implementation/TypedModifyHelper.cs +++ b/DynamORM/Builders/Implementation/TypedModifyHelper.cs @@ -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, string> _resolveColumn; private readonly Func _renderValue; private readonly Func _decorateName; + private readonly Func _renderSubQuery; - public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName) + public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName, Func 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(Expression> selector) @@ -67,12 +75,13 @@ namespace DynamORM.Builders.Implementation Func, TypedSqlPredicate> predicate, string alias, Func renderValue, - Func decorateName) + Func decorateName, + Func 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(alias)).Render(context); } @@ -80,12 +89,13 @@ namespace DynamORM.Builders.Implementation Func, TypedSqlExpression> expression, string alias, Func renderValue, - Func decorateName) + Func decorateName, + Func 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(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) diff --git a/DynamORM/TypedSql/ITypedSqlRenderContext.cs b/DynamORM/TypedSql/ITypedSqlRenderContext.cs index 58ed69d..203b3e0 100644 --- a/DynamORM/TypedSql/ITypedSqlRenderContext.cs +++ b/DynamORM/TypedSql/ITypedSqlRenderContext.cs @@ -19,5 +19,8 @@ namespace DynamORM.TypedSql /// Decorate SQL identifier. string DecorateName(string name); + + /// Render subquery SQL and merge any parameters into current context. + string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query); } } diff --git a/DynamORM/TypedSql/Sql.cs b/DynamORM/TypedSql/Sql.cs index 91f46d5..757eb6b 100644 --- a/DynamORM/TypedSql/Sql.cs +++ b/DynamORM/TypedSql/Sql.cs @@ -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("COALESCE", expressions); } + /// Create SUM expression. + public static TypedSqlExpression Sum(TypedSqlExpression expression) + { + return Func("SUM", expression); + } + + /// Create AVG expression. + public static TypedSqlExpression Avg(TypedSqlExpression expression) + { + return Func("AVG", expression); + } + + /// Create MIN expression. + public static TypedSqlExpression Min(TypedSqlExpression expression) + { + return Func("MIN", expression); + } + + /// Create MAX expression. + public static TypedSqlExpression Max(TypedSqlExpression expression) + { + return Func("MAX", expression); + } + + /// Create ABS expression. + public static TypedSqlExpression Abs(TypedSqlExpression expression) + { + return Func("ABS", expression); + } + + /// Create UPPER expression. + public static TypedSqlExpression Upper(TypedSqlExpression expression) + { + return Func("UPPER", expression); + } + + /// Create LOWER expression. + public static TypedSqlExpression Lower(TypedSqlExpression expression) + { + return Func("LOWER", expression); + } + + /// Create TRIM expression. + public static TypedSqlExpression Trim(TypedSqlExpression expression) + { + return Func("TRIM", expression); + } + + /// Create LENGTH expression. + public static TypedSqlExpression Length(TypedSqlExpression expression) + { + return Func("LENGTH", expression); + } + + /// Create NULLIF expression. + public static TypedSqlExpression NullIf(TypedSqlExpression left, TypedSqlExpression right) + { + return Func("NULLIF", left, right); + } + + /// Create CURRENT_TIMESTAMP expression. + public static TypedSqlExpression CurrentTimestamp() + { + return Raw("CURRENT_TIMESTAMP"); + } + + /// Create scalar subquery expression. + public static TypedSqlExpression SubQuery(IDynamicSelectQueryBuilder query) + { + return new TypedSqlSubQueryExpression(query); + } + + /// Create EXISTS predicate. + public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) + { + return new TypedSqlExistsPredicate(query); + } + /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { diff --git a/DynamORM/TypedSql/TypedSqlExpression.cs b/DynamORM/TypedSql/TypedSqlExpression.cs index 7676013..1bdf68e 100644 --- a/DynamORM/TypedSql/TypedSqlExpression.cs +++ b/DynamORM/TypedSql/TypedSqlExpression.cs @@ -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"); } + + /// LIKE predicate. + public TypedSqlPredicate Like(string pattern) + { + return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern)); + } + + /// IN predicate. + public TypedSqlPredicate In(params object[] values) + { + return new TypedSqlInPredicate(this, values); + } + + /// IN predicate. + public TypedSqlPredicate In(IEnumerable values) + { + return new TypedSqlInPredicate(this, values); + } + + /// BETWEEN predicate. + 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)); + } } /// Typed SQL expression. @@ -167,7 +193,7 @@ namespace DynamORM.TypedSql } } - internal sealed class TypedSqlValueExpression : TypedSqlExpression + internal sealed class TypedSqlValueExpression : TypedSqlExpression, 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 : TypedSqlExpression @@ -251,13 +282,62 @@ namespace DynamORM.TypedSql internal override string Render(ITypedSqlRenderContext context) { string op = _operator; - if (_right is TypedSqlValueExpression && string.Equals(_right.Render(context), "NULL", StringComparison.OrdinalIgnoreCase)) + TypedSqlValueExpression objRight = _right as TypedSqlValueExpression; + 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 rendered = new List(); + 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 : TypedSqlExpression + { + 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)); + } + } } From f293bd95c685750b9e76106a2cece841d5140678 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 08:36:47 +0100 Subject: [PATCH 12/20] Extend typed SQL DSL with pattern helpers and typed subqueries --- AmalgamationTool/DynamORM.Amalgamation.cs | 58 ++++++++++++++- DynamORM.Tests/TypedSql/TypedSqlDslTests.cs | 82 +++++++++++++++++++++ DynamORM/TypedSql/Sql.cs | 22 ++++++ DynamORM/TypedSql/TypedSqlExpression.cs | 38 +++++++++- DynamORM/TypedSql/TypedTableContext.cs | 6 ++ 5 files changed, 200 insertions(+), 6 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 1109c46..4ea5c27 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -15915,11 +15915,31 @@ namespace DynamORM { return new TypedSqlSubQueryExpression(query); } + /// Create scalar typed subquery expression without manually constructing the builder. + public static TypedSqlExpression SubQuery(DynamicDatabase db, Func, 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(configure(db.FromTyped(alias, noLock))); + } /// Create EXISTS predicate. public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) { return new TypedSqlExistsPredicate(query); } + /// Create EXISTS predicate from typed subquery factory. + public static TypedSqlPredicate Exists(DynamicDatabase db, Func, 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(alias, noLock))); + } /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { @@ -16029,11 +16049,36 @@ namespace DynamORM { return new TypedSqlInPredicate(this, values); } + /// NOT IN predicate. + public TypedSqlPredicate NotIn(params object[] values) + { + return new TypedSqlInPredicate(this, values, true); + } + /// NOT IN predicate. + public TypedSqlPredicate NotIn(IEnumerable values) + { + return new TypedSqlInPredicate(this, values, true); + } /// BETWEEN predicate. 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)); } + /// Starts-with LIKE predicate. + public TypedSqlPredicate StartsWith(string value) + { + return Like((value ?? string.Empty) + "%"); + } + /// Ends-with LIKE predicate. + public TypedSqlPredicate EndsWith(string value) + { + return Like("%" + (value ?? string.Empty)); + } + /// Contains LIKE predicate. + public TypedSqlPredicate Contains(string value) + { + return Like("%" + (value ?? string.Empty) + "%"); + } } /// Typed SQL expression. public abstract class TypedSqlExpression : 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(typeof(T), member.Member.Name, Alias); } + /// Select all columns from this typed table context. + public TypedSqlExpression All() + { + return Sql.Raw(string.IsNullOrEmpty(Alias) ? "*" : Alias + ".*"); + } } } namespace Validation diff --git a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs index 7b2e49c..54e7dc1 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs @@ -130,6 +130,44 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestWhereSqlSupportsNotIn() + { + var cmd = Database.FromTyped("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("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("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] public void TestWhereSqlSupportsEmptyIn() { @@ -175,6 +213,17 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestSelectSqlSupportsWildcardAll() + { + var cmd = Database.FromTyped("u") + .SelectSql(u => u.All()); + + Assert.AreEqual( + "SELECT u.* FROM \"sample_users\" AS u", + NormalizeSql(cmd.CommandText())); + } + [Test] public void TestSelectSqlSupportsScalarSubQuery() { @@ -206,6 +255,39 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestSelectSqlSupportsTypedSubQueryHelper() + { + var cmd = Database.FromTyped("u") + .SelectSql(u => Sql.SubQuery( + 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("u") + .WhereSql(u => Sql.Exists( + 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] public void TestInsertSqlObjectProjection() { diff --git a/DynamORM/TypedSql/Sql.cs b/DynamORM/TypedSql/Sql.cs index 757eb6b..265f96e 100644 --- a/DynamORM/TypedSql/Sql.cs +++ b/DynamORM/TypedSql/Sql.cs @@ -127,12 +127,34 @@ namespace DynamORM.TypedSql return new TypedSqlSubQueryExpression(query); } + /// Create scalar typed subquery expression without manually constructing the builder. + public static TypedSqlExpression SubQuery(DynamicDatabase db, Func, 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(configure(db.FromTyped(alias, noLock))); + } + /// Create EXISTS predicate. public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) { return new TypedSqlExistsPredicate(query); } + /// Create EXISTS predicate from typed subquery factory. + public static TypedSqlPredicate Exists(DynamicDatabase db, Func, 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(alias, noLock))); + } + /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { diff --git a/DynamORM/TypedSql/TypedSqlExpression.cs b/DynamORM/TypedSql/TypedSqlExpression.cs index 1bdf68e..ab08813 100644 --- a/DynamORM/TypedSql/TypedSqlExpression.cs +++ b/DynamORM/TypedSql/TypedSqlExpression.cs @@ -104,11 +104,41 @@ namespace DynamORM.TypedSql return new TypedSqlInPredicate(this, values); } + /// NOT IN predicate. + public TypedSqlPredicate NotIn(params object[] values) + { + return new TypedSqlInPredicate(this, values, true); + } + + /// NOT IN predicate. + public TypedSqlPredicate NotIn(IEnumerable values) + { + return new TypedSqlInPredicate(this, values, true); + } + /// BETWEEN predicate. 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)); } + + /// Starts-with LIKE predicate. + public TypedSqlPredicate StartsWith(string value) + { + return Like((value ?? string.Empty) + "%"); + } + + /// Ends-with LIKE predicate. + public TypedSqlPredicate EndsWith(string value) + { + return Like("%" + (value ?? string.Empty)); + } + + /// Contains LIKE predicate. + public TypedSqlPredicate Contains(string value) + { + return Like("%" + (value ?? string.Empty) + "%"); + } } /// Typed SQL expression. @@ -299,11 +329,13 @@ namespace DynamORM.TypedSql { 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) @@ -313,9 +345,9 @@ namespace DynamORM.TypedSql 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())); } } diff --git a/DynamORM/TypedSql/TypedTableContext.cs b/DynamORM/TypedSql/TypedTableContext.cs index 39adad9..4240141 100644 --- a/DynamORM/TypedSql/TypedTableContext.cs +++ b/DynamORM/TypedSql/TypedTableContext.cs @@ -36,5 +36,11 @@ namespace DynamORM.TypedSql return new TypedSqlColumnExpression(typeof(T), member.Member.Name, Alias); } + + /// Select all columns from this typed table context. + public TypedSqlExpression All() + { + return Sql.Raw(string.IsNullOrEmpty(Alias) ? "*" : Alias + ".*"); + } } } From c0fb2e5232436736adeaef1bcecefdee92d7dd09 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 08:49:00 +0100 Subject: [PATCH 13/20] Add arithmetic and joined-alias helpers to typed SQL DSL --- AmalgamationTool/DynamORM.Amalgamation.cs | 110 ++++++++++++++++++- DynamORM.Tests/TypedSql/TypedSqlDslTests.cs | 60 ++++++++++ DynamORM/TypedSql/Sql.cs | 12 ++ DynamORM/TypedSql/TypedSqlExpression.cs | 115 +++++++++++++++++++- 4 files changed, 291 insertions(+), 6 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 4ea5c27..6124e23 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -15830,11 +15830,21 @@ namespace DynamORM { return new TypedSqlValueExpression(value); } + /// Create typed table context for an explicitly named alias, including joined aliases. + public static TypedTableContext Table(string alias) + { + return new TypedTableContext(alias); + } /// Create raw SQL expression. public static TypedSqlExpression Raw(string sql) { return new TypedSqlRawExpression(sql); } + /// Create raw ORDER BY fragment. + public static TypedSqlOrderExpression RawOrder(string sql) + { + return new TypedSqlRawOrderExpression(Raw(sql)); + } /// Create generic function call. public static TypedSqlExpression Func(string name, params TypedSqlExpression[] arguments) { @@ -15979,6 +15989,31 @@ namespace DynamORM /// Base SQL expression for the typed DSL. public abstract class TypedSqlExpression : TypedSqlSelectable { + /// Add arithmetic expression. + public static TypedSqlExpression operator +(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "+", right); + } + /// Subtract arithmetic expression. + public static TypedSqlExpression operator -(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "-", right); + } + /// Multiply arithmetic expression. + public static TypedSqlExpression operator *(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "*", right); + } + /// Divide arithmetic expression. + public static TypedSqlExpression operator /(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "/", right); + } + /// Modulo arithmetic expression. + public static TypedSqlExpression operator %(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "%", right); + } /// Alias this expression in SELECT clause. public TypedSqlAliasedExpression As(string alias) { @@ -15994,6 +16029,31 @@ namespace DynamORM { return new TypedSqlOrderExpression(this, false); } + /// Add expression to another value. + public TypedSqlExpression Add(object value) + { + return new TypedSqlBinaryExpression(this, "+", value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Subtract another value. + public TypedSqlExpression Sub(object value) + { + return new TypedSqlBinaryExpression(this, "-", value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Multiply by another value. + public TypedSqlExpression Mul(object value) + { + return new TypedSqlBinaryExpression(this, "*", value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Divide by another value. + public TypedSqlExpression Div(object value) + { + return new TypedSqlBinaryExpression(this, "/", value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Modulo by another value. + public TypedSqlExpression Mod(object value) + { + return new TypedSqlBinaryExpression(this, "%", value as TypedSqlExpression ?? Sql.Val(value)); + } /// Equality predicate. public TypedSqlPredicate Eq(object value) { @@ -16120,19 +16180,63 @@ namespace DynamORM } } /// Ordered SQL expression. - public sealed class TypedSqlOrderExpression : TypedSqlSelectable + public class TypedSqlOrderExpression : TypedSqlSelectable { private readonly TypedSqlExpression _expression; private readonly bool _ascending; + private readonly string _nullOrdering; + private readonly bool _raw; - internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending) + internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending, string nullOrdering = null, bool raw = false) { _expression = expression; _ascending = ascending; + _nullOrdering = nullOrdering; + _raw = raw; + } + /// Append NULLS FIRST ordering. + public TypedSqlOrderExpression NullsFirst() + { + return new TypedSqlOrderExpression(_expression, _ascending, "NULLS FIRST", _raw); + } + /// Append NULLS LAST ordering. + public TypedSqlOrderExpression NullsLast() + { + return new TypedSqlOrderExpression(_expression, _ascending, "NULLS LAST", _raw); } internal override string Render(ITypedSqlRenderContext context) { - return string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + string rendered = _raw + ? _expression.Render(context) + : string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + if (!string.IsNullOrEmpty(_nullOrdering)) + rendered = string.Format("{0} {1}", rendered, _nullOrdering); + + return rendered; + } + } + internal sealed class TypedSqlRawOrderExpression : TypedSqlOrderExpression + { + internal TypedSqlRawOrderExpression(TypedSqlExpression expression) + : base(expression, true, null, true) + { + } + } + internal sealed class TypedSqlBinaryExpression : TypedSqlExpression + { + private readonly TypedSqlExpression _left; + private readonly string _operator; + private readonly TypedSqlExpression _right; + + internal TypedSqlBinaryExpression(TypedSqlExpression left, string op, TypedSqlExpression right) + { + _left = left; + _operator = op; + _right = right; + } + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); } } internal sealed class TypedSqlColumnExpression : TypedSqlExpression diff --git a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs index 54e7dc1..225e783 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs @@ -202,6 +202,22 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestSelectSqlSupportsArithmeticExpressions() + { + var cmd = Database.FromTyped("u") + .SelectSql( + u => (u.Col(x => x.Id) + Sql.Val(1)).As("plus_one"), + u => u.Col(x => x.Id).Sub(2).As("minus_two"), + u => u.Col(x => x.Id).Mul(3).As("times_three"), + u => u.Col(x => x.Id).Div(4).As("div_four"), + u => u.Col(x => x.Id).Mod(5).As("mod_five")); + + Assert.AreEqual( + "SELECT (u.\"id_user\" + [$0]) AS \"plus_one\", (u.\"id_user\" - [$1]) AS \"minus_two\", (u.\"id_user\" * [$2]) AS \"times_three\", (u.\"id_user\" / [$3]) AS \"div_four\", (u.\"id_user\" % [$4]) AS \"mod_five\" FROM \"sample_users\" AS u", + NormalizeSql(cmd.CommandText())); + } + [Test] public void TestSelectSqlSupportsCustomFunction() { @@ -224,6 +240,36 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestSelectSqlSupportsJoinedAliasHelpers() + { + var other = Sql.Table("x"); + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id)))) + .SelectSql( + u => u.All(), + u => other.All(), + u => other.Col(x => x.Code).As("joined_code")); + + Assert.AreEqual( + "SELECT u.*, x.*, x.\"user_code\" AS \"joined_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\")", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestWhereSqlSupportsJoinedAliasHelpers() + { + var other = Sql.Table("x"); + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("x").OnSql((l, r) => l.Col(a => a.Id).Eq(r.Col(a => a.Id)))) + .WhereSql(u => other.Col(x => x.Code).IsNotNull()) + .SelectSql(u => u.Col(x => x.Id)); + + Assert.AreEqual( + "SELECT u.\"id_user\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS x ON (u.\"id_user\" = x.\"id_user\") WHERE (x.\"user_code\" IS NOT NULL)", + NormalizeSql(cmd.CommandText())); + } + [Test] public void TestSelectSqlSupportsScalarSubQuery() { @@ -271,6 +317,20 @@ namespace DynamORM.Tests.TypedSql NormalizeSql(cmd.CommandText())); } + [Test] + public void TestOrderBySqlSupportsNullOrderingAndRawFragments() + { + var cmd = Database.FromTyped("u") + .SelectSql(u => u.Col(x => x.Id)) + .OrderBySql( + u => u.Col(x => x.Code).Asc().NullsLast(), + u => Sql.RawOrder("2 DESC")); + + Assert.AreEqual( + "SELECT u.\"id_user\" FROM \"sample_users\" AS u ORDER BY u.\"user_code\" ASC NULLS LAST, 2 DESC", + NormalizeSql(cmd.CommandText())); + } + [Test] public void TestWhereSqlSupportsTypedExistsHelper() { diff --git a/DynamORM/TypedSql/Sql.cs b/DynamORM/TypedSql/Sql.cs index 265f96e..eea24df 100644 --- a/DynamORM/TypedSql/Sql.cs +++ b/DynamORM/TypedSql/Sql.cs @@ -25,12 +25,24 @@ namespace DynamORM.TypedSql return new TypedSqlValueExpression(value); } + /// Create typed table context for an explicitly named alias, including joined aliases. + public static TypedTableContext Table(string alias) + { + return new TypedTableContext(alias); + } + /// Create raw SQL expression. public static TypedSqlExpression Raw(string sql) { return new TypedSqlRawExpression(sql); } + /// Create raw ORDER BY fragment. + public static TypedSqlOrderExpression RawOrder(string sql) + { + return new TypedSqlRawOrderExpression(Raw(sql)); + } + /// Create generic function call. public static TypedSqlExpression Func(string name, params TypedSqlExpression[] arguments) { diff --git a/DynamORM/TypedSql/TypedSqlExpression.cs b/DynamORM/TypedSql/TypedSqlExpression.cs index ab08813..a13c7d0 100644 --- a/DynamORM/TypedSql/TypedSqlExpression.cs +++ b/DynamORM/TypedSql/TypedSqlExpression.cs @@ -20,6 +20,36 @@ namespace DynamORM.TypedSql /// Base SQL expression for the typed DSL. public abstract class TypedSqlExpression : TypedSqlSelectable { + /// Add arithmetic expression. + public static TypedSqlExpression operator +(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "+", right); + } + + /// Subtract arithmetic expression. + public static TypedSqlExpression operator -(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "-", right); + } + + /// Multiply arithmetic expression. + public static TypedSqlExpression operator *(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "*", right); + } + + /// Divide arithmetic expression. + public static TypedSqlExpression operator /(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "/", right); + } + + /// Modulo arithmetic expression. + public static TypedSqlExpression operator %(TypedSqlExpression left, TypedSqlExpression right) + { + return new TypedSqlBinaryExpression(left, "%", right); + } + /// Alias this expression in SELECT clause. public TypedSqlAliasedExpression As(string alias) { @@ -38,6 +68,36 @@ namespace DynamORM.TypedSql return new TypedSqlOrderExpression(this, false); } + /// Add expression to another value. + public TypedSqlExpression Add(object value) + { + return new TypedSqlBinaryExpression(this, "+", value as TypedSqlExpression ?? Sql.Val(value)); + } + + /// Subtract another value. + public TypedSqlExpression Sub(object value) + { + return new TypedSqlBinaryExpression(this, "-", value as TypedSqlExpression ?? Sql.Val(value)); + } + + /// Multiply by another value. + public TypedSqlExpression Mul(object value) + { + return new TypedSqlBinaryExpression(this, "*", value as TypedSqlExpression ?? Sql.Val(value)); + } + + /// Divide by another value. + public TypedSqlExpression Div(object value) + { + return new TypedSqlBinaryExpression(this, "/", value as TypedSqlExpression ?? Sql.Val(value)); + } + + /// Modulo by another value. + public TypedSqlExpression Mod(object value) + { + return new TypedSqlBinaryExpression(this, "%", value as TypedSqlExpression ?? Sql.Val(value)); + } + /// Equality predicate. public TypedSqlPredicate Eq(object value) { @@ -187,20 +247,69 @@ namespace DynamORM.TypedSql } /// Ordered SQL expression. - public sealed class TypedSqlOrderExpression : TypedSqlSelectable + public class TypedSqlOrderExpression : TypedSqlSelectable { private readonly TypedSqlExpression _expression; private readonly bool _ascending; + private readonly string _nullOrdering; + private readonly bool _raw; - internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending) + internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending, string nullOrdering = null, bool raw = false) { _expression = expression; _ascending = ascending; + _nullOrdering = nullOrdering; + _raw = raw; + } + + /// Append NULLS FIRST ordering. + public TypedSqlOrderExpression NullsFirst() + { + return new TypedSqlOrderExpression(_expression, _ascending, "NULLS FIRST", _raw); + } + + /// Append NULLS LAST ordering. + public TypedSqlOrderExpression NullsLast() + { + return new TypedSqlOrderExpression(_expression, _ascending, "NULLS LAST", _raw); } internal override string Render(ITypedSqlRenderContext context) { - return string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + string rendered = _raw + ? _expression.Render(context) + : string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); + if (!string.IsNullOrEmpty(_nullOrdering)) + rendered = string.Format("{0} {1}", rendered, _nullOrdering); + + return rendered; + } + } + + internal sealed class TypedSqlRawOrderExpression : TypedSqlOrderExpression + { + internal TypedSqlRawOrderExpression(TypedSqlExpression expression) + : base(expression, true, null, true) + { + } + } + + internal sealed class TypedSqlBinaryExpression : TypedSqlExpression + { + private readonly TypedSqlExpression _left; + private readonly string _operator; + private readonly TypedSqlExpression _right; + + internal TypedSqlBinaryExpression(TypedSqlExpression left, string op, TypedSqlExpression right) + { + _left = left; + _operator = op; + _right = right; + } + + internal override string Render(ITypedSqlRenderContext context) + { + return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); } } From 8210fbfdb5178e0093a0deb88190fd6c17d8ede0 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 09:12:17 +0100 Subject: [PATCH 14/20] Add positional typed multi-table SQL contexts --- AmalgamationTool/DynamORM.Amalgamation.cs | 358 +++++++++++++++++- .../Helpers/TypedMultiJoinModels.cs | 36 ++ DynamORM.Tests/TypedSql/TypedSqlDslTests.cs | 112 ++++++ .../IDynamicTypedSelectQueryBuilder.cs | 60 +++ .../DynamicTypedSelectQueryBuilder.cs | 327 +++++++++++++++- 5 files changed, 865 insertions(+), 28 deletions(-) create mode 100644 DynamORM.Tests/Helpers/TypedMultiJoinModels.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 6124e23..7762576 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7303,6 +7303,66 @@ namespace DynamORM /// Add typed SQL DSL order by expressions. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL select items using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL where predicate using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL group-by expression using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } /// Typed update query builder for mapped entities. /// Mapped entity type. @@ -10869,6 +10929,11 @@ namespace DynamORM /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder { + private sealed class TypedJoinInfo + { + public Type ModelType { get; set; } + public string Alias { get; set; } + } private sealed class TypedSqlRenderContext : ITypedSqlRenderContext { private readonly DynamicTypedSelectQueryBuilder _builder; @@ -10916,6 +10981,7 @@ namespace DynamORM } } private readonly DynamicTypeMap _mapper; + private readonly List _typedJoins = new List(); public DynamicTypedSelectQueryBuilder(DynamicDatabase db) : base(db) @@ -10977,6 +11043,7 @@ namespace DynamORM if (!string.IsNullOrEmpty(condition)) joinExpr += string.Format(" ON {0}", condition); + RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } @@ -11019,6 +11086,7 @@ namespace DynamORM else if (!string.IsNullOrEmpty(spec.OnRawCondition)) joinExpr += string.Format(" ON {0}", spec.OnRawCondition); + RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } @@ -11076,6 +11144,54 @@ namespace DynamORM return this; } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -11105,6 +11221,54 @@ namespace DynamORM return this; } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -11134,6 +11298,54 @@ namespace DynamORM return this; } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } public new IDynamicTypedSelectQueryBuilder Top(int? top) { base.Top(top); @@ -11173,27 +11385,83 @@ namespace DynamORM if (predicate == null) throw new ArgumentNullException("predicate"); - string condition = RenderSqlPredicate(predicate); - if (string.IsNullOrEmpty(WhereCondition)) - WhereCondition = condition; - else - WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + AppendSqlCondition(RenderSqlPredicate(predicate), false); return this; } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), false); + return this; + } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), false); + return this; + } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), false); + return this; + } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), false); + return this; + } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); - string condition = RenderSqlPredicate(predicate); - if (string.IsNullOrEmpty(HavingCondition)) - HavingCondition = condition; - else - HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + AppendSqlCondition(RenderSqlPredicate(predicate), true); return this; } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), true); + return this; + } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), true); + return this; + } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), true); + return this; + } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), true); + return this; + } public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) { base.Having(column); @@ -11237,8 +11505,14 @@ namespace DynamORM if (selector == null) throw new ArgumentNullException("selector"); + AddSelectSqlSelector(selector(GetRootContext())); + } + private void AddSelectSqlSelector(TypedSqlSelectable item) + { + if (item == null) + throw new ArgumentNullException("item"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlSelectable item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered); } @@ -11265,8 +11539,14 @@ namespace DynamORM if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBySqlSelector(selector(GetRootContext())); + } + private void AddGroupBySqlSelector(TypedSqlExpression item) + { + if (item == null) + throw new ArgumentNullException("item"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered); } @@ -11289,15 +11569,25 @@ namespace DynamORM if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBySqlSelector(selector(GetRootContext())); + } + private void AddOrderBySqlSelector(TypedSqlOrderExpression item) + { TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlOrderExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered); } private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate) { + return RenderSqlPredicate(predicate(GetRootContext())); + } + private string RenderSqlPredicate(TypedSqlPredicate predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - return predicate(new TypedTableContext(GetRootAliasOrTableName())).Render(context); + return predicate.Render(context); } private string ParseTypedCondition(Expression expression) { @@ -11461,6 +11751,46 @@ namespace DynamORM return string.IsNullOrEmpty(table.Alias) ? table.Name : table.Alias; } + private TypedTableContext GetRootContext() + { + return new TypedTableContext(GetRootAliasOrTableName()); + } + private TypedTableContext GetJoinedContext(int index) + { + if (index < 0 || index >= _typedJoins.Count) + throw new InvalidOperationException(string.Format("Typed join context at position {0} is not available.", index + 1)); + + TypedJoinInfo join = _typedJoins[index]; + if (join.ModelType != typeof(TJoin)) + throw new InvalidOperationException(string.Format("Typed join context at position {0} is {1}, not {2}.", index + 1, join.ModelType.FullName, typeof(TJoin).FullName)); + + return new TypedTableContext(join.Alias); + } + private void RegisterTypedJoin(Type modelType, string alias) + { + _typedJoins.Add(new TypedJoinInfo + { + ModelType = modelType, + Alias = alias + }); + } + private void AppendSqlCondition(string condition, bool having) + { + if (having) + { + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + } + else + { + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + } + } private static bool IsMemberFromTypedParameter(Expression expression) { var member = expression as MemberExpression; diff --git a/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs b/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs new file mode 100644 index 0000000..b1fa421 --- /dev/null +++ b/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs @@ -0,0 +1,36 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using DynamORM.Mapper; + +namespace DynamORM.Tests.Helpers +{ + [Table(Name = "UserRoles", Owner = "dbo")] + public class TypedJoinUserRole + { + [Column("Role_Id", true)] + public long RoleId { get; set; } + + [Column("User_Id")] + public long UserId { get; set; } + + [Column("Role_Name")] + public string RoleName { get; set; } + } + + [Table(Name = "UserRegions", Owner = "dbo")] + public class TypedJoinUserRegion + { + [Column("Region_Id", true)] + public long RegionId { get; set; } + + [Column("User_Id")] + public long UserId { get; set; } + + [Column("Region_Code")] + public string RegionCode { get; set; } + } +} diff --git a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs index 225e783..37fb232 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlDslTests.cs @@ -386,5 +386,117 @@ namespace DynamORM.Tests.TypedSql "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())); } + + [Test] + public void TestSelectSqlSupportsPositionalJoinedContexts() + { + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .SelectSql( + (u, c) => u.Col(x => x.IdUser).As("user_id"), + (u, c) => c.Col(x => x.Users).As("client_users")); + + Assert.AreEqual( + "SELECT u.\"Id_User\" AS \"user_id\", c.\"Users\" AS \"client_users\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\")", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestWhereGroupHavingAndOrderBySupportPositionalJoinedContexts() + { + var cmd = Database.FromTyped("u") + .Join(j => j.Inner().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .SelectSql( + (u, c) => u.Col(x => x.IdUser), + (u, c) => Sql.Count(c.Col(x => x.UserId)).As("cnt")) + .WhereSql((u, c) => u.Col(x => x.Active).Eq(1).And(c.Col(x => x.Deleted).Eq(0))) + .GroupBySql( + (u, c) => u.Col(x => x.IdUser), + (u, c) => c.Col(x => x.Users)) + .HavingSql((u, c) => Sql.Count(c.Col(x => x.UserId)).Gt(0)) + .OrderBySql( + (u, c) => c.Col(x => x.Users).Asc(), + (u, c) => Sql.Count(c.Col(x => x.UserId)).Desc()); + + Assert.AreEqual( + "SELECT u.\"Id_User\", COUNT(c.\"User_Id\") AS \"cnt\" FROM \"dbo\".\"Users\" AS u INNER JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") WHERE ((u.\"Active\" = [$0]) AND (c.\"Deleted\" = [$1])) GROUP BY u.\"Id_User\", c.\"Users\" HAVING (COUNT(c.\"User_Id\") > [$2]) ORDER BY c.\"Users\" ASC, COUNT(c.\"User_Id\") DESC", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestPositionalJoinedContextsSupportThreeJoins() + { + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .Join(j => j.Left().As("r").OnSql((u, r) => u.Col(x => x.IdUser).Eq(r.Col(x => x.UserId)))) + .Join(j => j.Left().As("g").OnSql((u, g) => u.Col(x => x.IdUser).Eq(g.Col(x => x.UserId)))) + .SelectSql( + (u, c, r, g) => u.Col(x => x.IdUser), + (u, c, r, g) => c.Col(x => x.Users).As("client_users"), + (u, c, r, g) => r.Col(x => x.RoleName).As("role_name"), + (u, c, r, g) => g.Col(x => x.RegionCode).As("region_code")) + .WhereSql( + (u, c, r, g) => c.Col(x => x.Deleted).Eq(0).And(r.Col(x => x.RoleId).Gt(0)).And(g.Col(x => x.RegionId).Gt(0))) + .OrderBySql( + (u, c, r, g) => r.Col(x => x.RoleName).Asc(), + (u, c, r, g) => g.Col(x => x.RegionCode).Desc()); + + Assert.AreEqual( + "SELECT u.\"Id_User\", c.\"Users\" AS \"client_users\", r.\"Role_Name\" AS \"role_name\", g.\"Region_Code\" AS \"region_code\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (u.\"Id_User\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (u.\"Id_User\" = g.\"User_Id\") WHERE (((c.\"Deleted\" = [$0]) AND (r.\"Role_Id\" > [$1])) AND (g.\"Region_Id\" > [$2])) ORDER BY r.\"Role_Name\" ASC, g.\"Region_Code\" DESC", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestPositionalJoinedContextsSupportFourJoinedTables() + { + var cmd = Database.FromTyped("u") + .Join(j => j.Left().As("b").OnSql((u, b) => u.Col(x => x.Id).Eq(b.Col(x => x.Id)))) + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.Id).Eq(c.Col(x => x.Id)))) + .Join(j => j.Left().As("d").OnSql((u, d) => u.Col(x => x.Id).Eq(d.Col(x => x.Id)))) + .Join(j => j.Left().As("e").OnSql((u, e) => u.Col(x => x.Id).Eq(e.Col(x => x.Id)))) + .SelectSql( + (u, b, c, d, e) => u.Col(x => x.Id).As("root_id"), + (u, b, c, d, e) => b.Col(x => x.Code).As("b_code"), + (u, b, c, d, e) => c.Col(x => x.Code).As("c_code"), + (u, b, c, d, e) => d.Col(x => x.Code).As("d_code"), + (u, b, c, d, e) => e.Col(x => x.Code).As("e_code")) + .WhereSql( + (u, b, c, d, e) => b.Col(x => x.Code).IsNotNull() + .And(c.Col(x => x.Code).IsNotNull()) + .And(d.Col(x => x.Code).IsNotNull()) + .And(e.Col(x => x.Code).IsNotNull())); + + Assert.AreEqual( + "SELECT u.\"id_user\" AS \"root_id\", b.\"user_code\" AS \"b_code\", c.\"user_code\" AS \"c_code\", d.\"user_code\" AS \"d_code\", e.\"user_code\" AS \"e_code\" FROM \"sample_users\" AS u LEFT JOIN \"sample_users\" AS b ON (u.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (u.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (u.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (u.\"id_user\" = e.\"id_user\") WHERE ((((b.\"user_code\" IS NOT NULL) AND (c.\"user_code\" IS NOT NULL)) AND (d.\"user_code\" IS NOT NULL)) AND (e.\"user_code\" IS NOT NULL))", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestPositionalJoinedContextsValidateRequestedType() + { + var builder = Database.FromTyped("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))); + + var ex = Assert.Throws(() => + builder.SelectSql((u, r) => r.Col(x => x.RoleId).As("role_id"))); + + Assert.AreEqual( + "Typed join context at position 1 is DynamORM.Tests.Helpers.TypedJoinUserClient, not DynamORM.Tests.Helpers.TypedJoinUserRole.", + ex.Message); + } + + [Test] + public void TestPositionalJoinedContextsValidateRequestedPosition() + { + var builder = Database.FromTyped("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))); + + var ex = Assert.Throws(() => + builder.SelectSql((u, c, r) => r.Col(x => x.RoleId).As("role_id"))); + + Assert.AreEqual( + "Typed join context at position 2 is not available.", + ex.Message); + } } } diff --git a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs index f165c6c..9982dcf 100644 --- a/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectQueryBuilder.cs @@ -105,5 +105,65 @@ namespace DynamORM.Builders /// Add typed SQL DSL order by expressions. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL select items using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL select items using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + + /// Add typed SQL DSL where predicate using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL where predicate using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL having predicate using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + + /// Add typed SQL DSL group-by expression using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL group-by expression using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first two joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first three joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + + /// Add typed SQL DSL order-by expression using root and first four joined table contexts. + IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } } diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index c86b9d4..ef2227e 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -42,6 +42,12 @@ namespace DynamORM.Builders.Implementation /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder { + private sealed class TypedJoinInfo + { + public Type ModelType { get; set; } + public string Alias { get; set; } + } + private sealed class TypedSqlRenderContext : ITypedSqlRenderContext { private readonly DynamicTypedSelectQueryBuilder _builder; @@ -94,6 +100,7 @@ namespace DynamORM.Builders.Implementation } private readonly DynamicTypeMap _mapper; + private readonly List _typedJoins = new List(); public DynamicTypedSelectQueryBuilder(DynamicDatabase db) : base(db) @@ -158,6 +165,7 @@ namespace DynamORM.Builders.Implementation if (!string.IsNullOrEmpty(condition)) joinExpr += string.Format(" ON {0}", condition); + RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } @@ -201,6 +209,7 @@ namespace DynamORM.Builders.Implementation else if (!string.IsNullOrEmpty(spec.OnRawCondition)) joinExpr += string.Format(" ON {0}", spec.OnRawCondition); + RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } @@ -267,6 +276,58 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) + AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } + public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -299,6 +360,58 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) + AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } + public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) { if (selector == null) @@ -331,6 +444,58 @@ namespace DynamORM.Builders.Implementation return this; } + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); + + return this; + } + + public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + + AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + if (selectors != null) + foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) + AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); + + return this; + } + public new IDynamicTypedSelectQueryBuilder Top(int? top) { base.Top(top); @@ -375,29 +540,93 @@ namespace DynamORM.Builders.Implementation if (predicate == null) throw new ArgumentNullException("predicate"); - string condition = RenderSqlPredicate(predicate); - if (string.IsNullOrEmpty(WhereCondition)) - WhereCondition = condition; - else - WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + AppendSqlCondition(RenderSqlPredicate(predicate), false); return this; } + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), false); + return this; + } + + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), false); + return this; + } + + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), false); + return this; + } + + public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), false); + return this; + } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); - string condition = RenderSqlPredicate(predicate); - if (string.IsNullOrEmpty(HavingCondition)) - HavingCondition = condition; - else - HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + AppendSqlCondition(RenderSqlPredicate(predicate), true); return this; } + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), true); + return this; + } + + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), true); + return this; + } + + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), true); + return this; + } + + public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), true); + return this; + } + public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) { base.Having(column); @@ -446,8 +675,15 @@ namespace DynamORM.Builders.Implementation if (selector == null) throw new ArgumentNullException("selector"); + AddSelectSqlSelector(selector(GetRootContext())); + } + + private void AddSelectSqlSelector(TypedSqlSelectable item) + { + if (item == null) + throw new ArgumentNullException("item"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlSelectable item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered); } @@ -476,8 +712,15 @@ namespace DynamORM.Builders.Implementation if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBySqlSelector(selector(GetRootContext())); + } + + private void AddGroupBySqlSelector(TypedSqlExpression item) + { + if (item == null) + throw new ArgumentNullException("item"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered); } @@ -503,16 +746,28 @@ namespace DynamORM.Builders.Implementation if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBySqlSelector(selector(GetRootContext())); + } + + private void AddOrderBySqlSelector(TypedSqlOrderExpression item) + { TypedSqlRenderContext context = new TypedSqlRenderContext(this); - TypedSqlOrderExpression item = selector(new TypedTableContext(GetRootAliasOrTableName())); string rendered = item.Render(context); _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered); } private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate) { + return RenderSqlPredicate(predicate(GetRootContext())); + } + + private string RenderSqlPredicate(TypedSqlPredicate predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + TypedSqlRenderContext context = new TypedSqlRenderContext(this); - return predicate(new TypedTableContext(GetRootAliasOrTableName())).Render(context); + return predicate.Render(context); } private string ParseTypedCondition(Expression expression) @@ -690,6 +945,50 @@ namespace DynamORM.Builders.Implementation return string.IsNullOrEmpty(table.Alias) ? table.Name : table.Alias; } + private TypedTableContext GetRootContext() + { + return new TypedTableContext(GetRootAliasOrTableName()); + } + + private TypedTableContext GetJoinedContext(int index) + { + if (index < 0 || index >= _typedJoins.Count) + throw new InvalidOperationException(string.Format("Typed join context at position {0} is not available.", index + 1)); + + TypedJoinInfo join = _typedJoins[index]; + if (join.ModelType != typeof(TJoin)) + throw new InvalidOperationException(string.Format("Typed join context at position {0} is {1}, not {2}.", index + 1, join.ModelType.FullName, typeof(TJoin).FullName)); + + return new TypedTableContext(join.Alias); + } + + private void RegisterTypedJoin(Type modelType, string alias) + { + _typedJoins.Add(new TypedJoinInfo + { + ModelType = modelType, + Alias = alias + }); + } + + private void AppendSqlCondition(string condition, bool having) + { + if (having) + { + if (string.IsNullOrEmpty(HavingCondition)) + HavingCondition = condition; + else + HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); + } + else + { + if (string.IsNullOrEmpty(WhereCondition)) + WhereCondition = condition; + else + WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); + } + } + private static bool IsMemberFromTypedParameter(Expression expression) { var member = expression as MemberExpression; From 8555e1958c3e205c6642f4621e58c506446187d8 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 09:29:23 +0100 Subject: [PATCH 15/20] Add scoped typed multi-table select builder --- AmalgamationTool/DynamORM.Amalgamation.cs | 715 ++++++++++++++++++ .../Helpers/TypedMultiJoinModels.cs | 13 + .../TypedSql/TypedSqlScopeDslTests.cs | 126 +++ .../IDynamicTypedSelectScopeQueryBuilder.cs | 65 ++ .../DynamicTypedSelectQueryBuilder.cs | 5 + .../DynamicTypedSelectScopeQueryBuilder.cs | 553 ++++++++++++++ DynamORM/Builders/TypedScopeJoinBuilder.cs | 213 ++++++ DynamORM/DynamicDatabase.cs | 20 +- 8 files changed, 1706 insertions(+), 4 deletions(-) create mode 100644 DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs create mode 100644 DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs create mode 100644 DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs create mode 100644 DynamORM/Builders/TypedScopeJoinBuilder.cs diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 7762576..51e5c7c 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -1977,6 +1977,17 @@ namespace DynamORM } return builder; } + /// Adds to the FROM clause using a typed scope builder with evolving join arity. + /// Type which can be represented in database. + /// Table alias. + /// use no lock. + /// Scope builder instance. + public virtual IDynamicTypedSelectScopeQueryBuilder FromTypedScope(string alias = null, bool noLock = false) + { + DynamicTypedSelectQueryBuilder builder = (DynamicTypedSelectQueryBuilder)FromTyped(alias, noLock); + string resolvedAlias = string.IsNullOrEmpty(alias) ? builder.Tables[0].Alias ?? builder.Tables[0].Name : alias; + return new DynamicTypedSelectScopeQueryBuilder(builder, resolvedAlias); + } /// Adds to the FROM clause using . /// Type which can be represented in database. /// This instance to permit chaining. @@ -7364,6 +7375,55 @@ namespace DynamORM /// Add typed SQL DSL order-by expression using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } + /// Typed scope-based select builder with evolving join arity. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + } + /// Typed scope-based select builder with two typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + /// Typed scope-based select builder with three typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + /// Typed scope-based select builder with four typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + /// Typed scope-based select builder with five typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } /// Typed update query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder @@ -7753,6 +7813,188 @@ namespace DynamORM return builder.Join(on, alias, DynamicJoinType.Full); } } + public abstract class TypedScopeJoinBuilderBase + where TSelf : TypedScopeJoinBuilderBase + { + /// Gets join alias. + public string Alias { get; private set; } + + /// Gets join type. + public DynamicJoinType JoinType { get; private set; } + + /// Gets custom join type text. + public string CustomJoinType { get; private set; } + + /// Gets a value indicating whether joined source should use NOLOCK. + public bool UseNoLock { get; private set; } + + protected TypedScopeJoinBuilderBase() + { + JoinType = DynamicJoinType.Join; + } + public TSelf As(string alias) + { + Alias = alias; + return (TSelf)this; + } + public TSelf Inner() + { + JoinType = DynamicJoinType.Inner; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf Join() + { + JoinType = DynamicJoinType.Join; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf Left() + { + JoinType = DynamicJoinType.Left; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf Right() + { + JoinType = DynamicJoinType.Right; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf Full() + { + JoinType = DynamicJoinType.Full; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf LeftOuter() + { + JoinType = DynamicJoinType.LeftOuter; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf RightOuter() + { + JoinType = DynamicJoinType.RightOuter; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf FullOuter() + { + JoinType = DynamicJoinType.FullOuter; + CustomJoinType = null; + return (TSelf)this; + } + public TSelf Type(string joinType) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); + + CustomJoinType = joinType.Trim(); + return (TSelf)this; + } + public TSelf NoLock(bool use = true) + { + UseNoLock = use; + return (TSelf)this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } namespace Extensions { internal static class DynamicHavingQueryExtensions @@ -11589,6 +11831,10 @@ namespace DynamORM TypedSqlRenderContext context = new TypedSqlRenderContext(this); return predicate.Render(context); } + internal string RenderScopeSqlPredicate(TypedSqlPredicate predicate) + { + return RenderSqlPredicate(predicate); + } private string ParseTypedCondition(Expression expression) { expression = UnwrapConvert(expression); @@ -11981,6 +12227,475 @@ namespace DynamORM return getter(); } } + internal abstract class DynamicTypedSelectScopeQueryBuilderBase : IDynamicSelectQueryBuilder + { + protected readonly DynamicTypedSelectQueryBuilder Builder; + + protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) + { + Builder = builder; + } + public DynamicDatabase Database { get { return Builder.Database; } } + public IList Tables { get { return Builder.Tables; } } + public IDictionary Parameters { get { return Builder.Parameters; } } + public bool VirtualMode { get { return Builder.VirtualMode; } set { Builder.VirtualMode = value; } } + public bool SupportSchema { get { return Builder.SupportSchema; } } + public List> OnCreateTemporaryParameter { get { return Builder.OnCreateTemporaryParameter; } set { Builder.OnCreateTemporaryParameter = value; } } + public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } + public bool IsDisposed { get { return Builder.IsDisposed; } } + + public void Dispose() + { + Builder.Dispose(); + } + public IDbCommand FillCommand(IDbCommand command) + { + return Builder.FillCommand(command); + } + public string CommandText() + { + return Builder.CommandText(); + } + public IEnumerable Execute() + { + return Builder.Execute(); + } + public IEnumerable Execute() where T : class + { + return Builder.Execute(); + } + public void ExecuteDataReader(Action reader) + { + Builder.ExecuteDataReader(reader); + } + public object Scalar() + { + return Builder.Scalar(); + } + #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE + public TResult ScalarAs(TResult defaultValue = default(TResult)) + { + return Builder.ScalarAs(defaultValue); + } + #endif + + public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) + { + Builder.From(fn, func); + return this; + } + public IDynamicSelectQueryBuilder Join(params Func[] func) + { + Builder.Join(func); + return this; + } + public IDynamicSelectQueryBuilder Where(Func func) + { + Builder.Where(func); + return this; + } + public IDynamicSelectQueryBuilder Where(DynamicColumn column) + { + Builder.Where(column); + return this; + } + public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + Builder.Where(column, op, value); + return this; + } + public IDynamicSelectQueryBuilder Where(string column, object value) + { + Builder.Where(column, value); + return this; + } + public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) + { + Builder.Where(conditions, schema); + return this; + } + public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) + { + Builder.Select(fn, func); + return this; + } + public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) + { + Builder.SelectColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) + { + Builder.SelectColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) + { + Builder.GroupBy(fn, func); + return this; + } + public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) + { + Builder.GroupByColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) + { + Builder.GroupByColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder Having(Func func) + { + Builder.Having(func); + return this; + } + public IDynamicSelectQueryBuilder Having(DynamicColumn column) + { + Builder.Having(column); + return this; + } + public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) + { + Builder.Having(column, op, value); + return this; + } + public IDynamicSelectQueryBuilder Having(string column, object value) + { + Builder.Having(column, value); + return this; + } + public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) + { + Builder.Having(conditions, schema); + return this; + } + public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) + { + Builder.OrderBy(fn, func); + return this; + } + public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) + { + Builder.OrderByColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) + { + Builder.OrderByColumn(columns); + return this; + } + public IDynamicSelectQueryBuilder Top(int? top) + { + Builder.Top(top); + return this; + } + public IDynamicSelectQueryBuilder Limit(int? limit) + { + Builder.Limit(limit); + return this; + } + public IDynamicSelectQueryBuilder Offset(int? offset) + { + Builder.Offset(offset); + return this; + } + public IDynamicSelectQueryBuilder Distinct(bool distinct = true) + { + Builder.Distinct(distinct); + return this; + } + protected string GetJoinKeyword(DynamicJoinType joinType) + { + switch (joinType) + { + case DynamicJoinType.Join: + return "JOIN"; + case DynamicJoinType.Left: + return "LEFT JOIN"; + case DynamicJoinType.Right: + return "RIGHT JOIN"; + case DynamicJoinType.Full: + return "FULL JOIN"; + case DynamicJoinType.LeftOuter: + return "LEFT OUTER JOIN"; + case DynamicJoinType.RightOuter: + return "RIGHT OUTER JOIN"; + case DynamicJoinType.FullOuter: + return "FULL OUTER JOIN"; + default: + return "INNER JOIN"; + } + } + protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) + { + if (!string.IsNullOrEmpty(alias)) + builder.As(alias); + + if (!string.IsNullOrEmpty(customJoinType)) + builder.Type(customJoinType); + else + { + switch (joinType) + { + case DynamicJoinType.Join: builder.Join(); break; + case DynamicJoinType.Left: builder.Left(); break; + case DynamicJoinType.Right: builder.Right(); break; + case DynamicJoinType.Full: builder.Full(); break; + case DynamicJoinType.LeftOuter: builder.LeftOuter(); break; + case DynamicJoinType.RightOuter: builder.RightOuter(); break; + case DynamicJoinType.FullOuter: builder.FullOuter(); break; + default: builder.Inner(); break; + } + } + if (noLock) + builder.NoLock(); + + if (!string.IsNullOrEmpty(condition)) + builder.OnRaw(condition); + + return builder; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) + : base(builder) + { + _alias1 = alias1; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t3" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t4" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t5" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) + : base(builder) + { + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder diff --git a/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs b/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs index b1fa421..4e1c2ca 100644 --- a/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs +++ b/DynamORM.Tests/Helpers/TypedMultiJoinModels.cs @@ -33,4 +33,17 @@ namespace DynamORM.Tests.Helpers [Column("Region_Code")] public string RegionCode { get; set; } } + + [Table(Name = "UserDepartments", Owner = "dbo")] + public class TypedJoinUserDepartment + { + [Column("Department_Id", true)] + public long DepartmentId { get; set; } + + [Column("Region_Id")] + public long RegionId { get; set; } + + [Column("Department_Name")] + public string DepartmentName { get; set; } + } } diff --git a/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs new file mode 100644 index 0000000..bab0937 --- /dev/null +++ b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs @@ -0,0 +1,126 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System.Text.RegularExpressions; +using DynamORM.Tests.Helpers; +using DynamORM.TypedSql; +using NUnit.Framework; + +namespace DynamORM.Tests.TypedSql +{ + [TestFixture] + public class TypedSqlScopeDslTests : TestsBase + { + private static string NormalizeSql(string sql) + { + int index = 0; + return Regex.Replace(sql, @"\[\$[^\]]+\]", m => string.Format("[${0}]", index++)); + } + + [SetUp] + public void SetUp() + { + CreateTestDatabase(); + CreateDynamicDatabase( + DynamicDatabaseOptions.SingleConnection | + DynamicDatabaseOptions.SingleTransaction | + DynamicDatabaseOptions.SupportLimitOffset | + DynamicDatabaseOptions.SupportNoLock); + } + + [TearDown] + public void TearDown() + { + DestroyDynamicDatabase(); + DestroyTestDatabase(); + } + + [Test] + public void TestScopeBuilderSupportsTwoTableSelectAndWhere() + { + var cmd = Database.FromTypedScope("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .SelectSql( + (u, c) => u.Col(x => x.IdUser).As("user_id"), + (u, c) => c.Col(x => x.Users).As("users")) + .WhereSql((u, c) => u.Col(x => x.Active).Eq(1).And(c.Col(x => x.Deleted).Eq(0))); + + Assert.AreEqual( + "SELECT u.\"Id_User\" AS \"user_id\", c.\"Users\" AS \"users\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") WHERE ((u.\"Active\" = [$0]) AND (c.\"Deleted\" = [$1]))", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeBuilderSupportsJoinOnPreviousJoinedAlias() + { + var cmd = Database.FromTypedScope("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .Join(j => j.Left().As("r").OnSql((u, c, r) => c.Col(x => x.UserId).Eq(r.Col(x => x.UserId)))) + .SelectSql( + (u, c, r) => u.Col(x => x.IdUser), + (u, c, r) => c.Col(x => x.Users).As("users"), + (u, c, r) => r.Col(x => x.RoleName).As("role_name")) + .OrderBySql( + (u, c, r) => c.Col(x => x.Users).Asc(), + (u, c, r) => r.Col(x => x.RoleName).Desc()); + + Assert.AreEqual( + "SELECT u.\"Id_User\", c.\"Users\" AS \"users\", r.\"Role_Name\" AS \"role_name\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\") ORDER BY c.\"Users\" ASC, r.\"Role_Name\" DESC", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeBuilderSupportsCustomJoinTypeAndNoLock() + { + var cmd = Database.FromTypedScope("u") + .Join(j => j.Type("CROSS APPLY").As("c").NoLock()) + .SelectSql( + (u, c) => u.Col(x => x.IdUser), + (u, c) => c.All()); + + Assert.AreEqual( + "SELECT u.\"Id_User\", c.* FROM \"dbo\".\"Users\" AS u CROSS APPLY \"dbo\".\"UserClients\" AS c WITH(NOLOCK)", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeBuilderSupportsHavingAcrossFourJoinedTables() + { + var cmd = Database.FromTypedScope("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .Join(j => j.Left().As("r").OnSql((u, c, r) => u.Col(x => x.IdUser).Eq(r.Col(x => x.UserId)))) + .Join(j => j.Left().As("g").OnSql((u, c, r, g) => r.Col(x => x.UserId).Eq(g.Col(x => x.UserId)))) + .SelectSql( + (u, c, r, g) => u.Col(x => x.IdUser), + (u, c, r, g) => Sql.Count(g.Col(x => x.RegionId)).As("region_count")) + .GroupBySql((u, c, r, g) => u.Col(x => x.IdUser)) + .HavingSql((u, c, r, g) => Sql.Count(g.Col(x => x.RegionId)).Gt(0)); + + Assert.AreEqual( + "SELECT u.\"Id_User\", COUNT(g.\"Region_Id\") AS \"region_count\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (u.\"Id_User\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (r.\"User_Id\" = g.\"User_Id\") GROUP BY u.\"Id_User\" HAVING (COUNT(g.\"Region_Id\") > [$0])", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeBuilderSupportsFiveTypedTables() + { + var cmd = Database.FromTypedScope("u") + .Join(j => j.Left().As("c").OnSql((u, c) => u.Col(x => x.IdUser).Eq(c.Col(x => x.UserId)))) + .Join(j => j.Left().As("r").OnSql((u, c, r) => c.Col(x => x.UserId).Eq(r.Col(x => x.UserId)))) + .Join(j => j.Left().As("g").OnSql((u, c, r, g) => r.Col(x => x.UserId).Eq(g.Col(x => x.UserId)))) + .Join(j => j.Left().As("d").OnSql((u, c, r, g, d) => g.Col(x => x.RegionId).Eq(d.Col(x => x.RegionId)))) + .SelectSql( + (u, c, r, g, d) => u.Col(x => x.IdUser).As("user_id"), + (u, c, r, g, d) => d.Col(x => x.DepartmentName).As("department_name")) + .WhereSql((u, c, r, g, d) => d.Col(x => x.DepartmentId).Gt(0)) + .OrderBySql((u, c, r, g, d) => d.Col(x => x.DepartmentName).Asc()); + + Assert.AreEqual( + "SELECT u.\"Id_User\" AS \"user_id\", d.\"Department_Name\" AS \"department_name\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (r.\"User_Id\" = g.\"User_Id\") LEFT JOIN \"dbo\".\"UserDepartments\" AS d ON (g.\"Region_Id\" = d.\"Region_Id\") WHERE (d.\"Department_Id\" > [$0]) ORDER BY d.\"Department_Name\" ASC", + NormalizeSql(cmd.CommandText())); + } + } +} diff --git a/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs new file mode 100644 index 0000000..9802ea9 --- /dev/null +++ b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs @@ -0,0 +1,65 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using DynamORM.TypedSql; + +namespace DynamORM.Builders +{ + /// Typed scope-based select builder with evolving join arity. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + } + + /// Typed scope-based select builder with two typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + /// Typed scope-based select builder with three typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + /// Typed scope-based select builder with four typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + /// Typed scope-based select builder with five typed table contexts. + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } +} diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index ef2227e..7bed59e 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -770,6 +770,11 @@ namespace DynamORM.Builders.Implementation return predicate.Render(context); } + internal string RenderScopeSqlPredicate(TypedSqlPredicate predicate) + { + return RenderSqlPredicate(predicate); + } + private string ParseTypedCondition(Expression expression) { expression = UnwrapConvert(expression); diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs new file mode 100644 index 0000000..f2bb702 --- /dev/null +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs @@ -0,0 +1,553 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using DynamORM.Helpers; +using DynamORM.TypedSql; + +namespace DynamORM.Builders.Implementation +{ + internal abstract class DynamicTypedSelectScopeQueryBuilderBase : IDynamicSelectQueryBuilder + { + protected readonly DynamicTypedSelectQueryBuilder Builder; + + protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) + { + Builder = builder; + } + + public DynamicDatabase Database { get { return Builder.Database; } } + public IList Tables { get { return Builder.Tables; } } + public IDictionary Parameters { get { return Builder.Parameters; } } + public bool VirtualMode { get { return Builder.VirtualMode; } set { Builder.VirtualMode = value; } } + public bool SupportSchema { get { return Builder.SupportSchema; } } + public List> OnCreateTemporaryParameter { get { return Builder.OnCreateTemporaryParameter; } set { Builder.OnCreateTemporaryParameter = value; } } + public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } + public bool IsDisposed { get { return Builder.IsDisposed; } } + + public void Dispose() + { + Builder.Dispose(); + } + + public IDbCommand FillCommand(IDbCommand command) + { + return Builder.FillCommand(command); + } + + public string CommandText() + { + return Builder.CommandText(); + } + + public IEnumerable Execute() + { + return Builder.Execute(); + } + + public IEnumerable Execute() where T : class + { + return Builder.Execute(); + } + + public void ExecuteDataReader(Action reader) + { + Builder.ExecuteDataReader(reader); + } + + public object Scalar() + { + return Builder.Scalar(); + } + +#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE + public TResult ScalarAs(TResult defaultValue = default(TResult)) + { + return Builder.ScalarAs(defaultValue); + } +#endif + + public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) + { + Builder.From(fn, func); + return this; + } + + public IDynamicSelectQueryBuilder Join(params Func[] func) + { + Builder.Join(func); + return this; + } + + public IDynamicSelectQueryBuilder Where(Func func) + { + Builder.Where(func); + return this; + } + + public IDynamicSelectQueryBuilder Where(DynamicColumn column) + { + Builder.Where(column); + return this; + } + + public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) + { + Builder.Where(column, op, value); + return this; + } + + public IDynamicSelectQueryBuilder Where(string column, object value) + { + Builder.Where(column, value); + return this; + } + + public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) + { + Builder.Where(conditions, schema); + return this; + } + + public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) + { + Builder.Select(fn, func); + return this; + } + + public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) + { + Builder.SelectColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) + { + Builder.SelectColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) + { + Builder.GroupBy(fn, func); + return this; + } + + public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) + { + Builder.GroupByColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) + { + Builder.GroupByColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder Having(Func func) + { + Builder.Having(func); + return this; + } + + public IDynamicSelectQueryBuilder Having(DynamicColumn column) + { + Builder.Having(column); + return this; + } + + public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) + { + Builder.Having(column, op, value); + return this; + } + + public IDynamicSelectQueryBuilder Having(string column, object value) + { + Builder.Having(column, value); + return this; + } + + public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) + { + Builder.Having(conditions, schema); + return this; + } + + public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) + { + Builder.OrderBy(fn, func); + return this; + } + + public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) + { + Builder.OrderByColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) + { + Builder.OrderByColumn(columns); + return this; + } + + public IDynamicSelectQueryBuilder Top(int? top) + { + Builder.Top(top); + return this; + } + + public IDynamicSelectQueryBuilder Limit(int? limit) + { + Builder.Limit(limit); + return this; + } + + public IDynamicSelectQueryBuilder Offset(int? offset) + { + Builder.Offset(offset); + return this; + } + + public IDynamicSelectQueryBuilder Distinct(bool distinct = true) + { + Builder.Distinct(distinct); + return this; + } + + protected string GetJoinKeyword(DynamicJoinType joinType) + { + switch (joinType) + { + case DynamicJoinType.Join: + return "JOIN"; + case DynamicJoinType.Left: + return "LEFT JOIN"; + case DynamicJoinType.Right: + return "RIGHT JOIN"; + case DynamicJoinType.Full: + return "FULL JOIN"; + case DynamicJoinType.LeftOuter: + return "LEFT OUTER JOIN"; + case DynamicJoinType.RightOuter: + return "RIGHT OUTER JOIN"; + case DynamicJoinType.FullOuter: + return "FULL OUTER JOIN"; + default: + return "INNER JOIN"; + } + } + + protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) + { + if (!string.IsNullOrEmpty(alias)) + builder.As(alias); + + if (!string.IsNullOrEmpty(customJoinType)) + builder.Type(customJoinType); + else + { + switch (joinType) + { + case DynamicJoinType.Join: builder.Join(); break; + case DynamicJoinType.Left: builder.Left(); break; + case DynamicJoinType.Right: builder.Right(); break; + case DynamicJoinType.Full: builder.Full(); break; + case DynamicJoinType.LeftOuter: builder.LeftOuter(); break; + case DynamicJoinType.RightOuter: builder.RightOuter(); break; + case DynamicJoinType.FullOuter: builder.FullOuter(); break; + default: builder.Inner(); break; + } + } + + if (noLock) + builder.NoLock(); + + if (!string.IsNullOrEmpty(condition)) + builder.OnRaw(condition); + + return builder; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) + : base(builder) + { + _alias1 = alias1; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t3" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t4" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t5" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) + : base(builder) + { + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + Builder.SelectSql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.WhereSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + Builder.HavingSql(predicate); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + Builder.GroupBySql(selector, selectors); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + Builder.OrderBySql(selector, selectors); + return this; + } + } +} diff --git a/DynamORM/Builders/TypedScopeJoinBuilder.cs b/DynamORM/Builders/TypedScopeJoinBuilder.cs new file mode 100644 index 0000000..8be2509 --- /dev/null +++ b/DynamORM/Builders/TypedScopeJoinBuilder.cs @@ -0,0 +1,213 @@ +/* + * DynamORM - Dynamic Object-Relational Mapping library. + * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) + * All rights reserved. + */ + +using System; +using DynamORM.TypedSql; + +namespace DynamORM.Builders +{ + public abstract class TypedScopeJoinBuilderBase + where TSelf : TypedScopeJoinBuilderBase + { + /// Gets join alias. + public string Alias { get; private set; } + + /// Gets join type. + public DynamicJoinType JoinType { get; private set; } + + /// Gets custom join type text. + public string CustomJoinType { get; private set; } + + /// Gets a value indicating whether joined source should use NOLOCK. + public bool UseNoLock { get; private set; } + + protected TypedScopeJoinBuilderBase() + { + JoinType = DynamicJoinType.Join; + } + + public TSelf As(string alias) + { + Alias = alias; + return (TSelf)this; + } + + public TSelf Inner() + { + JoinType = DynamicJoinType.Inner; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf Join() + { + JoinType = DynamicJoinType.Join; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf Left() + { + JoinType = DynamicJoinType.Left; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf Right() + { + JoinType = DynamicJoinType.Right; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf Full() + { + JoinType = DynamicJoinType.Full; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf LeftOuter() + { + JoinType = DynamicJoinType.LeftOuter; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf RightOuter() + { + JoinType = DynamicJoinType.RightOuter; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf FullOuter() + { + JoinType = DynamicJoinType.FullOuter; + CustomJoinType = null; + return (TSelf)this; + } + + public TSelf Type(string joinType) + { + if (string.IsNullOrEmpty(joinType)) + throw new ArgumentNullException("joinType"); + + CustomJoinType = joinType.Trim(); + return (TSelf)this; + } + + public TSelf NoLock(bool use = true) + { + UseNoLock = use; + return (TSelf)this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } +} diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index 83b4e9b..0735cc5 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -477,8 +477,8 @@ namespace DynamORM /// Table alias. /// use no lock. /// This instance to permit chaining. - public virtual IDynamicTypedSelectQueryBuilder FromTyped(string alias = null, bool noLock = false) - { + public virtual IDynamicTypedSelectQueryBuilder FromTyped(string alias = null, bool noLock = false) + { // TODO: Make it more readable and maitainable DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); @@ -497,8 +497,20 @@ namespace DynamORM builder.From(x => x(typeof(T)).As(alias)); } - return builder; - } + return builder; + } + + /// Adds to the FROM clause using a typed scope builder with evolving join arity. + /// Type which can be represented in database. + /// Table alias. + /// use no lock. + /// Scope builder instance. + public virtual IDynamicTypedSelectScopeQueryBuilder FromTypedScope(string alias = null, bool noLock = false) + { + DynamicTypedSelectQueryBuilder builder = (DynamicTypedSelectQueryBuilder)FromTyped(alias, noLock); + string resolvedAlias = string.IsNullOrEmpty(alias) ? builder.Tables[0].Alias ?? builder.Tables[0].Name : alias; + return new DynamicTypedSelectScopeQueryBuilder(builder, resolvedAlias); + } /// Adds to the FROM clause using . /// Type which can be represented in database. From e0ed9ed895c3737c04f2e463dbde122ed9e8ce5d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 09:53:20 +0100 Subject: [PATCH 16/20] Extend scoped typed select builder to deep join chains --- AmalgamationTool/DynamORM.Amalgamation.cs | 1049 ++++++++++++----- .../TypedSql/TypedSqlScopeDslTests.cs | 24 + .../IDynamicTypedSelectScopeQueryBuilder.cs | 72 +- .../DynamicTypedSelectQueryBuilder.cs | 8 +- .../DynamicTypedSelectScopeQueryBuilder.cs | 869 ++++++++++---- DynamORM/Builders/TypedScopeJoinBuilder.cs | 202 ++-- 6 files changed, 1597 insertions(+), 627 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 51e5c7c..7289be2 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7375,17 +7375,15 @@ namespace DynamORM /// Add typed SQL DSL order-by expression using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with evolving join arity. - public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { - IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); - IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); - IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); - IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); - IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); - IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with two typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -7395,7 +7393,6 @@ namespace DynamORM IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with three typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -7405,7 +7402,6 @@ namespace DynamORM IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with four typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -7415,15 +7411,59 @@ namespace DynamORM IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with five typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } /// Typed update query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder @@ -7816,16 +7856,9 @@ namespace DynamORM public abstract class TypedScopeJoinBuilderBase where TSelf : TypedScopeJoinBuilderBase { - /// Gets join alias. public string Alias { get; private set; } - - /// Gets join type. public DynamicJoinType JoinType { get; private set; } - - /// Gets custom join type text. public string CustomJoinType { get; private set; } - - /// Gets a value indicating whether joined source should use NOLOCK. public bool UseNoLock { get; private set; } protected TypedScopeJoinBuilderBase() @@ -7837,59 +7870,19 @@ namespace DynamORM Alias = alias; return (TSelf)this; } - public TSelf Inner() - { - JoinType = DynamicJoinType.Inner; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf Join() - { - JoinType = DynamicJoinType.Join; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf Left() - { - JoinType = DynamicJoinType.Left; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf Right() - { - JoinType = DynamicJoinType.Right; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf Full() - { - JoinType = DynamicJoinType.Full; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf LeftOuter() - { - JoinType = DynamicJoinType.LeftOuter; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf RightOuter() - { - JoinType = DynamicJoinType.RightOuter; - CustomJoinType = null; - return (TSelf)this; - } - public TSelf FullOuter() - { - JoinType = DynamicJoinType.FullOuter; - CustomJoinType = null; - return (TSelf)this; - } + public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; } + public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; } + public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; } + public TSelf Right() { JoinType = DynamicJoinType.Right; CustomJoinType = null; return (TSelf)this; } + public TSelf Full() { JoinType = DynamicJoinType.Full; CustomJoinType = null; return (TSelf)this; } + public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; } + public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; } + public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; } + public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); - CustomJoinType = joinType.Trim(); return (TSelf)this; } @@ -7908,7 +7901,6 @@ namespace DynamORM { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -7917,7 +7909,6 @@ namespace DynamORM { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -7932,7 +7923,6 @@ namespace DynamORM { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -7941,7 +7931,6 @@ namespace DynamORM { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -7956,7 +7945,6 @@ namespace DynamORM { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -7965,7 +7953,6 @@ namespace DynamORM { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -7980,7 +7967,6 @@ namespace DynamORM { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -7989,7 +7975,116 @@ namespace DynamORM { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -11749,7 +11844,7 @@ namespace DynamORM AddSelectSqlSelector(selector(GetRootContext())); } - private void AddSelectSqlSelector(TypedSqlSelectable item) + internal void AddSelectSqlSelector(TypedSqlSelectable item) { if (item == null) throw new ArgumentNullException("item"); @@ -11783,7 +11878,7 @@ namespace DynamORM AddGroupBySqlSelector(selector(GetRootContext())); } - private void AddGroupBySqlSelector(TypedSqlExpression item) + internal void AddGroupBySqlSelector(TypedSqlExpression item) { if (item == null) throw new ArgumentNullException("item"); @@ -11813,7 +11908,7 @@ namespace DynamORM AddOrderBySqlSelector(selector(GetRootContext())); } - private void AddOrderBySqlSelector(TypedSqlOrderExpression item) + internal void AddOrderBySqlSelector(TypedSqlOrderExpression item) { TypedSqlRenderContext context = new TypedSqlRenderContext(this); string rendered = item.Render(context); @@ -12020,7 +12115,7 @@ namespace DynamORM Alias = alias }); } - private void AppendSqlCondition(string condition, bool having) + internal void AppendSqlCondition(string condition, bool having) { if (having) { @@ -12244,193 +12339,46 @@ namespace DynamORM public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } public bool IsDisposed { get { return Builder.IsDisposed; } } - public void Dispose() - { - Builder.Dispose(); - } - public IDbCommand FillCommand(IDbCommand command) - { - return Builder.FillCommand(command); - } - public string CommandText() - { - return Builder.CommandText(); - } - public IEnumerable Execute() - { - return Builder.Execute(); - } - public IEnumerable Execute() where T : class - { - return Builder.Execute(); - } - public void ExecuteDataReader(Action reader) - { - Builder.ExecuteDataReader(reader); - } - public object Scalar() - { - return Builder.Scalar(); - } + public void Dispose() { Builder.Dispose(); } + public IDbCommand FillCommand(IDbCommand command) { return Builder.FillCommand(command); } + public string CommandText() { return Builder.CommandText(); } + public IEnumerable Execute() { return Builder.Execute(); } + public IEnumerable Execute() where T : class { return Builder.Execute(); } + public void ExecuteDataReader(Action reader) { Builder.ExecuteDataReader(reader); } + public object Scalar() { return Builder.Scalar(); } #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE - public TResult ScalarAs(TResult defaultValue = default(TResult)) - { - return Builder.ScalarAs(defaultValue); - } + public TResult ScalarAs(TResult defaultValue = default(TResult)) { return Builder.ScalarAs(defaultValue); } #endif + public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) { Builder.From(fn, func); return this; } + public IDynamicSelectQueryBuilder Join(params Func[] func) { Builder.Join(func); return this; } + public IDynamicSelectQueryBuilder Where(Func func) { Builder.Where(func); return this; } + public IDynamicSelectQueryBuilder Where(DynamicColumn column) { Builder.Where(column); return this; } + public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { Builder.Where(column, op, value); return this; } + public IDynamicSelectQueryBuilder Where(string column, object value) { Builder.Where(column, value); return this; } + public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) { Builder.Where(conditions, schema); return this; } + public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) { Builder.Select(fn, func); return this; } + public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) { Builder.SelectColumn(columns); return this; } + public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) { Builder.SelectColumn(columns); return this; } + public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) { Builder.GroupBy(fn, func); return this; } + public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) { Builder.GroupByColumn(columns); return this; } + public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) { Builder.GroupByColumn(columns); return this; } + public IDynamicSelectQueryBuilder Having(Func func) { Builder.Having(func); return this; } + public IDynamicSelectQueryBuilder Having(DynamicColumn column) { Builder.Having(column); return this; } + public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) { Builder.Having(column, op, value); return this; } + public IDynamicSelectQueryBuilder Having(string column, object value) { Builder.Having(column, value); return this; } + public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) { Builder.Having(conditions, schema); return this; } + public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) { Builder.OrderBy(fn, func); return this; } + public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) { Builder.OrderByColumn(columns); return this; } + public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) { Builder.OrderByColumn(columns); return this; } + public IDynamicSelectQueryBuilder Top(int? top) { Builder.Top(top); return this; } + public IDynamicSelectQueryBuilder Limit(int? limit) { Builder.Limit(limit); return this; } + public IDynamicSelectQueryBuilder Offset(int? offset) { Builder.Offset(offset); return this; } + public IDynamicSelectQueryBuilder Distinct(bool distinct = true) { Builder.Distinct(distinct); return this; } - public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) - { - Builder.From(fn, func); - return this; - } - public IDynamicSelectQueryBuilder Join(params Func[] func) - { - Builder.Join(func); - return this; - } - public IDynamicSelectQueryBuilder Where(Func func) - { - Builder.Where(func); - return this; - } - public IDynamicSelectQueryBuilder Where(DynamicColumn column) - { - Builder.Where(column); - return this; - } - public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) - { - Builder.Where(column, op, value); - return this; - } - public IDynamicSelectQueryBuilder Where(string column, object value) - { - Builder.Where(column, value); - return this; - } - public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) - { - Builder.Where(conditions, schema); - return this; - } - public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) - { - Builder.Select(fn, func); - return this; - } - public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) - { - Builder.SelectColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) - { - Builder.SelectColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) - { - Builder.GroupBy(fn, func); - return this; - } - public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) - { - Builder.GroupByColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) - { - Builder.GroupByColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder Having(Func func) - { - Builder.Having(func); - return this; - } - public IDynamicSelectQueryBuilder Having(DynamicColumn column) - { - Builder.Having(column); - return this; - } - public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) - { - Builder.Having(column, op, value); - return this; - } - public IDynamicSelectQueryBuilder Having(string column, object value) - { - Builder.Having(column, value); - return this; - } - public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) - { - Builder.Having(conditions, schema); - return this; - } - public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) - { - Builder.OrderBy(fn, func); - return this; - } - public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) - { - Builder.OrderByColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) - { - Builder.OrderByColumn(columns); - return this; - } - public IDynamicSelectQueryBuilder Top(int? top) - { - Builder.Top(top); - return this; - } - public IDynamicSelectQueryBuilder Limit(int? limit) - { - Builder.Limit(limit); - return this; - } - public IDynamicSelectQueryBuilder Offset(int? offset) - { - Builder.Offset(offset); - return this; - } - public IDynamicSelectQueryBuilder Distinct(bool distinct = true) - { - Builder.Distinct(distinct); - return this; - } - protected string GetJoinKeyword(DynamicJoinType joinType) - { - switch (joinType) - { - case DynamicJoinType.Join: - return "JOIN"; - case DynamicJoinType.Left: - return "LEFT JOIN"; - case DynamicJoinType.Right: - return "RIGHT JOIN"; - case DynamicJoinType.Full: - return "FULL JOIN"; - case DynamicJoinType.LeftOuter: - return "LEFT OUTER JOIN"; - case DynamicJoinType.RightOuter: - return "RIGHT OUTER JOIN"; - case DynamicJoinType.FullOuter: - return "FULL OUTER JOIN"; - default: - return "INNER JOIN"; - } - } protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) { if (!string.IsNullOrEmpty(alias)) builder.As(alias); - if (!string.IsNullOrEmpty(customJoinType)) builder.Type(customJoinType); else @@ -12449,12 +12397,15 @@ namespace DynamORM } if (noLock) builder.NoLock(); - if (!string.IsNullOrEmpty(condition)) builder.OnRaw(condition); - return builder; } + protected void AddSelect(TypedSqlSelectable item) { Builder.AddSelectSqlSelector(item); } + protected void AddGroupBy(TypedSqlExpression item) { Builder.AddGroupBySqlSelector(item); } + protected void AddOrderBy(TypedSqlOrderExpression item) { Builder.AddOrderBySqlSelector(item); } + protected void AddWhere(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), false); } + protected void AddHaving(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), true); } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { @@ -12465,43 +12416,62 @@ namespace DynamORM { _alias1 = alias1; } - public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); - TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; - string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); - Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); - return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1))); return this; } } @@ -12532,27 +12502,46 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } } @@ -12585,27 +12574,46 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } } @@ -12640,59 +12648,518 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) : base(builder) { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t6" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t7" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t8" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t9" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t10" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } } diff --git a/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs index bab0937..95b21f5 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs @@ -122,5 +122,29 @@ namespace DynamORM.Tests.TypedSql "SELECT u.\"Id_User\" AS \"user_id\", d.\"Department_Name\" AS \"department_name\" FROM \"dbo\".\"Users\" AS u LEFT JOIN \"dbo\".\"UserClients\" AS c ON (u.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\") LEFT JOIN \"dbo\".\"UserRegions\" AS g ON (r.\"User_Id\" = g.\"User_Id\") LEFT JOIN \"dbo\".\"UserDepartments\" AS d ON (g.\"Region_Id\" = d.\"Region_Id\") WHERE (d.\"Department_Id\" > [$0]) ORDER BY d.\"Department_Name\" ASC", NormalizeSql(cmd.CommandText())); } + + [Test] + public void TestScopeBuilderSupportsTenTypedTables() + { + var cmd = Database.FromTypedScope("a") + .Join(j => j.Left().As("b").OnSql((a, b) => a.Col(x => x.Id).Eq(b.Col(x => x.Id)))) + .Join(j => j.Left().As("c").OnSql((a, b, c) => b.Col(x => x.Id).Eq(c.Col(x => x.Id)))) + .Join(j => j.Left().As("d").OnSql((a, b, c, d) => c.Col(x => x.Id).Eq(d.Col(x => x.Id)))) + .Join(j => j.Left().As("e").OnSql((a, b, c, d, e) => d.Col(x => x.Id).Eq(e.Col(x => x.Id)))) + .Join(j => j.Left().As("f").OnSql((a, b, c, d, e, f) => e.Col(x => x.Id).Eq(f.Col(x => x.Id)))) + .Join(j => j.Left().As("g").OnSql((a, b, c, d, e, f, g) => f.Col(x => x.Id).Eq(g.Col(x => x.Id)))) + .Join(j => j.Left().As("h").OnSql((a, b, c, d, e, f, g, h) => g.Col(x => x.Id).Eq(h.Col(x => x.Id)))) + .Join(j => j.Left().As("i").OnSql((a, b, c, d, e, f, g, h, i) => h.Col(x => x.Id).Eq(i.Col(x => x.Id)))) + .Join(j => j.Left().As("j").OnSql((a, b, c, d, e, f, g, h, i, j) => i.Col(x => x.Id).Eq(j.Col(x => x.Id)))) + .SelectSql( + (a, b, c, d, e, f, g, h, i, j) => a.Col(x => x.Id).As("root_id"), + (a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).As("last_code")) + .WhereSql((a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).IsNotNull()) + .OrderBySql((a, b, c, d, e, f, g, h, i, j) => j.Col(x => x.Code).Asc()); + + Assert.AreEqual( + "SELECT a.\"id_user\" AS \"root_id\", j.\"user_code\" AS \"last_code\" FROM \"sample_users\" AS a LEFT JOIN \"sample_users\" AS b ON (a.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (b.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (c.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (d.\"id_user\" = e.\"id_user\") LEFT JOIN \"sample_users\" AS f ON (e.\"id_user\" = f.\"id_user\") LEFT JOIN \"sample_users\" AS g ON (f.\"id_user\" = g.\"id_user\") LEFT JOIN \"sample_users\" AS h ON (g.\"id_user\" = h.\"id_user\") LEFT JOIN \"sample_users\" AS i ON (h.\"id_user\" = i.\"id_user\") LEFT JOIN \"sample_users\" AS j ON (i.\"id_user\" = j.\"id_user\") WHERE (j.\"user_code\" IS NOT NULL) ORDER BY j.\"user_code\" ASC", + NormalizeSql(cmd.CommandText())); + } } } diff --git a/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs index 9802ea9..78e6e9c 100644 --- a/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs @@ -9,18 +9,16 @@ using DynamORM.TypedSql; namespace DynamORM.Builders { - /// Typed scope-based select builder with evolving join arity. - public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { - IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); - IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); - IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); - IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); - IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); - IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with two typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -31,7 +29,6 @@ namespace DynamORM.Builders IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with three typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -42,7 +39,6 @@ namespace DynamORM.Builders IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with four typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); @@ -53,13 +49,63 @@ namespace DynamORM.Builders IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } - /// Typed scope-based select builder with five typed table contexts. public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } -} + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + +} \ No newline at end of file diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs index 7bed59e..5b02ad4 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectQueryBuilder.cs @@ -678,7 +678,7 @@ namespace DynamORM.Builders.Implementation AddSelectSqlSelector(selector(GetRootContext())); } - private void AddSelectSqlSelector(TypedSqlSelectable item) + internal void AddSelectSqlSelector(TypedSqlSelectable item) { if (item == null) throw new ArgumentNullException("item"); @@ -715,7 +715,7 @@ namespace DynamORM.Builders.Implementation AddGroupBySqlSelector(selector(GetRootContext())); } - private void AddGroupBySqlSelector(TypedSqlExpression item) + internal void AddGroupBySqlSelector(TypedSqlExpression item) { if (item == null) throw new ArgumentNullException("item"); @@ -749,7 +749,7 @@ namespace DynamORM.Builders.Implementation AddOrderBySqlSelector(selector(GetRootContext())); } - private void AddOrderBySqlSelector(TypedSqlOrderExpression item) + internal void AddOrderBySqlSelector(TypedSqlOrderExpression item) { TypedSqlRenderContext context = new TypedSqlRenderContext(this); string rendered = item.Render(context); @@ -976,7 +976,7 @@ namespace DynamORM.Builders.Implementation }); } - private void AppendSqlCondition(string condition, bool having) + internal void AppendSqlCondition(string condition, bool having) { if (having) { diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs index f2bb702..3e4e532 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs @@ -30,226 +30,46 @@ namespace DynamORM.Builders.Implementation public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } public bool IsDisposed { get { return Builder.IsDisposed; } } - public void Dispose() - { - Builder.Dispose(); - } - - public IDbCommand FillCommand(IDbCommand command) - { - return Builder.FillCommand(command); - } - - public string CommandText() - { - return Builder.CommandText(); - } - - public IEnumerable Execute() - { - return Builder.Execute(); - } - - public IEnumerable Execute() where T : class - { - return Builder.Execute(); - } - - public void ExecuteDataReader(Action reader) - { - Builder.ExecuteDataReader(reader); - } - - public object Scalar() - { - return Builder.Scalar(); - } - + public void Dispose() { Builder.Dispose(); } + public IDbCommand FillCommand(IDbCommand command) { return Builder.FillCommand(command); } + public string CommandText() { return Builder.CommandText(); } + public IEnumerable Execute() { return Builder.Execute(); } + public IEnumerable Execute() where T : class { return Builder.Execute(); } + public void ExecuteDataReader(Action reader) { Builder.ExecuteDataReader(reader); } + public object Scalar() { return Builder.Scalar(); } #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE - public TResult ScalarAs(TResult defaultValue = default(TResult)) - { - return Builder.ScalarAs(defaultValue); - } + public TResult ScalarAs(TResult defaultValue = default(TResult)) { return Builder.ScalarAs(defaultValue); } #endif - - public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) - { - Builder.From(fn, func); - return this; - } - - public IDynamicSelectQueryBuilder Join(params Func[] func) - { - Builder.Join(func); - return this; - } - - public IDynamicSelectQueryBuilder Where(Func func) - { - Builder.Where(func); - return this; - } - - public IDynamicSelectQueryBuilder Where(DynamicColumn column) - { - Builder.Where(column); - return this; - } - - public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) - { - Builder.Where(column, op, value); - return this; - } - - public IDynamicSelectQueryBuilder Where(string column, object value) - { - Builder.Where(column, value); - return this; - } - - public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) - { - Builder.Where(conditions, schema); - return this; - } - - public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) - { - Builder.Select(fn, func); - return this; - } - - public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) - { - Builder.SelectColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) - { - Builder.SelectColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) - { - Builder.GroupBy(fn, func); - return this; - } - - public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) - { - Builder.GroupByColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) - { - Builder.GroupByColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder Having(Func func) - { - Builder.Having(func); - return this; - } - - public IDynamicSelectQueryBuilder Having(DynamicColumn column) - { - Builder.Having(column); - return this; - } - - public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) - { - Builder.Having(column, op, value); - return this; - } - - public IDynamicSelectQueryBuilder Having(string column, object value) - { - Builder.Having(column, value); - return this; - } - - public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) - { - Builder.Having(conditions, schema); - return this; - } - - public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) - { - Builder.OrderBy(fn, func); - return this; - } - - public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) - { - Builder.OrderByColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) - { - Builder.OrderByColumn(columns); - return this; - } - - public IDynamicSelectQueryBuilder Top(int? top) - { - Builder.Top(top); - return this; - } - - public IDynamicSelectQueryBuilder Limit(int? limit) - { - Builder.Limit(limit); - return this; - } - - public IDynamicSelectQueryBuilder Offset(int? offset) - { - Builder.Offset(offset); - return this; - } - - public IDynamicSelectQueryBuilder Distinct(bool distinct = true) - { - Builder.Distinct(distinct); - return this; - } - - protected string GetJoinKeyword(DynamicJoinType joinType) - { - switch (joinType) - { - case DynamicJoinType.Join: - return "JOIN"; - case DynamicJoinType.Left: - return "LEFT JOIN"; - case DynamicJoinType.Right: - return "RIGHT JOIN"; - case DynamicJoinType.Full: - return "FULL JOIN"; - case DynamicJoinType.LeftOuter: - return "LEFT OUTER JOIN"; - case DynamicJoinType.RightOuter: - return "RIGHT OUTER JOIN"; - case DynamicJoinType.FullOuter: - return "FULL OUTER JOIN"; - default: - return "INNER JOIN"; - } - } + public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) { Builder.From(fn, func); return this; } + public IDynamicSelectQueryBuilder Join(params Func[] func) { Builder.Join(func); return this; } + public IDynamicSelectQueryBuilder Where(Func func) { Builder.Where(func); return this; } + public IDynamicSelectQueryBuilder Where(DynamicColumn column) { Builder.Where(column); return this; } + public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { Builder.Where(column, op, value); return this; } + public IDynamicSelectQueryBuilder Where(string column, object value) { Builder.Where(column, value); return this; } + public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) { Builder.Where(conditions, schema); return this; } + public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) { Builder.Select(fn, func); return this; } + public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) { Builder.SelectColumn(columns); return this; } + public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) { Builder.SelectColumn(columns); return this; } + public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) { Builder.GroupBy(fn, func); return this; } + public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) { Builder.GroupByColumn(columns); return this; } + public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) { Builder.GroupByColumn(columns); return this; } + public IDynamicSelectQueryBuilder Having(Func func) { Builder.Having(func); return this; } + public IDynamicSelectQueryBuilder Having(DynamicColumn column) { Builder.Having(column); return this; } + public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) { Builder.Having(column, op, value); return this; } + public IDynamicSelectQueryBuilder Having(string column, object value) { Builder.Having(column, value); return this; } + public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) { Builder.Having(conditions, schema); return this; } + public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) { Builder.OrderBy(fn, func); return this; } + public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) { Builder.OrderByColumn(columns); return this; } + public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) { Builder.OrderByColumn(columns); return this; } + public IDynamicSelectQueryBuilder Top(int? top) { Builder.Top(top); return this; } + public IDynamicSelectQueryBuilder Limit(int? limit) { Builder.Limit(limit); return this; } + public IDynamicSelectQueryBuilder Offset(int? offset) { Builder.Offset(offset); return this; } + public IDynamicSelectQueryBuilder Distinct(bool distinct = true) { Builder.Distinct(distinct); return this; } protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) { if (!string.IsNullOrEmpty(alias)) builder.As(alias); - if (!string.IsNullOrEmpty(customJoinType)) builder.Type(customJoinType); else @@ -266,15 +86,18 @@ namespace DynamORM.Builders.Implementation default: builder.Inner(); break; } } - if (noLock) builder.NoLock(); - if (!string.IsNullOrEmpty(condition)) builder.OnRaw(condition); - return builder; } + + protected void AddSelect(TypedSqlSelectable item) { Builder.AddSelectSqlSelector(item); } + protected void AddGroupBy(TypedSqlExpression item) { Builder.AddGroupBySqlSelector(item); } + protected void AddOrderBy(TypedSqlOrderExpression item) { Builder.AddOrderBySqlSelector(item); } + protected void AddWhere(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), false); } + protected void AddHaving(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), true); } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -287,48 +110,67 @@ namespace DynamORM.Builders.Implementation _alias1 = alias1; } - public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); - TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; - string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); - Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); - return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1))); return this; } } @@ -362,31 +204,50 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } } @@ -422,31 +283,50 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } } @@ -484,70 +364,565 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) : base(builder) { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t6" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - Builder.SelectSql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.WhereSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - Builder.HavingSql(predicate); + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - Builder.GroupBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - Builder.OrderBySql(selector, selectors); + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } } -} + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t7" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t8" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t9" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) + throw new ArgumentNullException("specification"); + + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) + throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + + string alias = string.IsNullOrEmpty(spec.Alias) ? "t10" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); + return this; + } + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) + : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) + throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); + return this; + } + } + +} \ No newline at end of file diff --git a/DynamORM/Builders/TypedScopeJoinBuilder.cs b/DynamORM/Builders/TypedScopeJoinBuilder.cs index 8be2509..7f00084 100644 --- a/DynamORM/Builders/TypedScopeJoinBuilder.cs +++ b/DynamORM/Builders/TypedScopeJoinBuilder.cs @@ -12,16 +12,9 @@ namespace DynamORM.Builders public abstract class TypedScopeJoinBuilderBase where TSelf : TypedScopeJoinBuilderBase { - /// Gets join alias. public string Alias { get; private set; } - - /// Gets join type. public DynamicJoinType JoinType { get; private set; } - - /// Gets custom join type text. public string CustomJoinType { get; private set; } - - /// Gets a value indicating whether joined source should use NOLOCK. public bool UseNoLock { get; private set; } protected TypedScopeJoinBuilderBase() @@ -35,67 +28,19 @@ namespace DynamORM.Builders return (TSelf)this; } - public TSelf Inner() - { - JoinType = DynamicJoinType.Inner; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf Join() - { - JoinType = DynamicJoinType.Join; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf Left() - { - JoinType = DynamicJoinType.Left; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf Right() - { - JoinType = DynamicJoinType.Right; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf Full() - { - JoinType = DynamicJoinType.Full; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf LeftOuter() - { - JoinType = DynamicJoinType.LeftOuter; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf RightOuter() - { - JoinType = DynamicJoinType.RightOuter; - CustomJoinType = null; - return (TSelf)this; - } - - public TSelf FullOuter() - { - JoinType = DynamicJoinType.FullOuter; - CustomJoinType = null; - return (TSelf)this; - } + public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; } + public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; } + public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; } + public TSelf Right() { JoinType = DynamicJoinType.Right; CustomJoinType = null; return (TSelf)this; } + public TSelf Full() { JoinType = DynamicJoinType.Full; CustomJoinType = null; return (TSelf)this; } + public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; } + public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; } + public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; } public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); - CustomJoinType = joinType.Trim(); return (TSelf)this; } @@ -116,7 +61,6 @@ namespace DynamORM.Builders { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -126,7 +70,6 @@ namespace DynamORM.Builders { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -142,7 +85,6 @@ namespace DynamORM.Builders { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -152,7 +94,6 @@ namespace DynamORM.Builders { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -168,7 +109,6 @@ namespace DynamORM.Builders { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -178,7 +118,6 @@ namespace DynamORM.Builders { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; @@ -194,7 +133,6 @@ namespace DynamORM.Builders { if (predicate == null) throw new ArgumentNullException("predicate"); - OnSqlPredicate = predicate; OnRawCondition = null; return this; @@ -204,10 +142,130 @@ namespace DynamORM.Builders { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); - OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } -} + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + +} \ No newline at end of file From 3797505c9c4cea2a9cd6e50c2388476837dcb181 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 10:43:37 +0100 Subject: [PATCH 17/20] Extend scoped typed API and add scoped subquery helpers --- AmalgamationTool/DynamORM.Amalgamation.cs | 1001 +++++++++++++---- .../TypedSql/TypedSqlScopeDslTests.cs | 64 ++ .../IDynamicTypedSelectScopeQueryBuilder.cs | 60 + .../DynamicTypedSelectScopeQueryBuilder.cs | 834 ++++++++++---- DynamORM/Builders/TypedScopeJoinBuilder.cs | 167 ++- DynamORM/TypedSql/Sql.cs | 22 + 6 files changed, 1710 insertions(+), 438 deletions(-) diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index 7289be2..7fb9fe2 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -7458,12 +7458,66 @@ namespace DynamORM } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } /// Typed update query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder @@ -7865,11 +7919,7 @@ namespace DynamORM { JoinType = DynamicJoinType.Join; } - public TSelf As(string alias) - { - Alias = alias; - return (TSelf)this; - } + public TSelf As(string alias) { Alias = alias; return (TSelf)this; } public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; } public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; } public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; } @@ -7878,19 +7928,8 @@ namespace DynamORM public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; } public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; } public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; } - - public TSelf Type(string joinType) - { - if (string.IsNullOrEmpty(joinType)) - throw new ArgumentNullException("joinType"); - CustomJoinType = joinType.Trim(); - return (TSelf)this; - } - public TSelf NoLock(bool use = true) - { - UseNoLock = use; - return (TSelf)this; - } + public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); CustomJoinType = joinType.Trim(); return (TSelf)this; } + public TSelf NoLock(bool use = true) { UseNoLock = use; return (TSelf)this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { @@ -8090,6 +8129,138 @@ namespace DynamORM return this; } } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } namespace Extensions { internal static class DynamicHavingQueryExtensions @@ -12325,11 +12496,7 @@ namespace DynamORM internal abstract class DynamicTypedSelectScopeQueryBuilderBase : IDynamicSelectQueryBuilder { protected readonly DynamicTypedSelectQueryBuilder Builder; - - protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) - { - Builder = builder; - } + protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) { Builder = builder; } public DynamicDatabase Database { get { return Builder.Database; } } public IList Tables { get { return Builder.Tables; } } public IDictionary Parameters { get { return Builder.Parameters; } } @@ -12338,7 +12505,6 @@ namespace DynamORM public List> OnCreateTemporaryParameter { get { return Builder.OnCreateTemporaryParameter; } set { Builder.OnCreateTemporaryParameter = value; } } public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } public bool IsDisposed { get { return Builder.IsDisposed; } } - public void Dispose() { Builder.Dispose(); } public IDbCommand FillCommand(IDbCommand command) { return Builder.FillCommand(command); } public string CommandText() { return Builder.CommandText(); } @@ -12374,13 +12540,10 @@ namespace DynamORM public IDynamicSelectQueryBuilder Limit(int? limit) { Builder.Limit(limit); return this; } public IDynamicSelectQueryBuilder Offset(int? offset) { Builder.Offset(offset); return this; } public IDynamicSelectQueryBuilder Distinct(bool distinct = true) { Builder.Distinct(distinct); return this; } - protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) { - if (!string.IsNullOrEmpty(alias)) - builder.As(alias); - if (!string.IsNullOrEmpty(customJoinType)) - builder.Type(customJoinType); + if (!string.IsNullOrEmpty(alias)) builder.As(alias); + if (!string.IsNullOrEmpty(customJoinType)) builder.Type(customJoinType); else { switch (joinType) @@ -12395,10 +12558,8 @@ namespace DynamORM default: builder.Inner(); break; } } - if (noLock) - builder.NoLock(); - if (!string.IsNullOrEmpty(condition)) - builder.OnRaw(condition); + if (noLock) builder.NoLock(); + if (!string.IsNullOrEmpty(condition)) builder.OnRaw(condition); return builder; } protected void AddSelect(TypedSqlSelectable item) { Builder.AddSelectSqlSelector(item); } @@ -12410,21 +12571,15 @@ namespace DynamORM internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) : base(builder) { _alias1 = alias1; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12432,8 +12587,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) @@ -12442,22 +12596,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) @@ -12466,8 +12617,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) @@ -12479,22 +12629,16 @@ namespace DynamORM { private readonly string _alias1; private readonly string _alias2; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) : base(builder) { _alias1 = alias1; _alias2 = alias2; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t3" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12502,8 +12646,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) @@ -12512,22 +12655,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) @@ -12536,8 +12676,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) @@ -12550,9 +12689,7 @@ namespace DynamORM private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12560,13 +12697,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t4" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12574,8 +12707,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) @@ -12584,22 +12716,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) @@ -12608,8 +12737,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) @@ -12623,9 +12751,7 @@ namespace DynamORM private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12634,13 +12760,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t5" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12648,8 +12770,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) @@ -12658,22 +12779,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) @@ -12682,8 +12800,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) @@ -12698,9 +12815,7 @@ namespace DynamORM private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12710,13 +12825,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t6" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12724,8 +12835,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) @@ -12734,22 +12844,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) @@ -12758,8 +12865,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) @@ -12775,9 +12881,7 @@ namespace DynamORM private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12788,13 +12892,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t7" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12802,8 +12902,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) @@ -12812,22 +12911,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) @@ -12836,8 +12932,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) @@ -12854,9 +12949,7 @@ namespace DynamORM private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12868,13 +12961,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t8" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12882,8 +12971,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) @@ -12892,22 +12980,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) @@ -12916,8 +13001,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) @@ -12935,9 +13019,7 @@ namespace DynamORM private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -12950,13 +13032,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t9" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -12964,8 +13042,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) @@ -12974,22 +13051,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) @@ -12998,8 +13072,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) @@ -13018,9 +13091,7 @@ namespace DynamORM private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -13034,13 +13105,9 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t10" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -13048,8 +13115,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) @@ -13058,22 +13124,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) @@ -13082,8 +13145,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) @@ -13103,9 +13165,7 @@ namespace DynamORM private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -13118,10 +13178,19 @@ namespace DynamORM _alias9 = alias9; _alias10 = alias10; } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t11" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, alias); + } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) @@ -13130,22 +13199,19 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) @@ -13154,8 +13220,7 @@ namespace DynamORM } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) @@ -13163,6 +13228,488 @@ namespace DynamORM return this; } } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t12" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t13" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t14" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t15" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + private readonly string _alias15; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + _alias15 = alias15; + } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t16" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, _alias15, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + } + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + private readonly string _alias15; + private readonly string _alias16; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15, string alias16) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + _alias15 = alias15; + _alias16 = alias16; + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder @@ -17447,6 +17994,16 @@ namespace DynamORM return new TypedSqlSubQueryExpression(configure(db.FromTyped(alias, noLock))); } + /// Create scalar typed subquery expression using the scoped typed builder API. + public static TypedSqlExpression SubQueryScope(DynamicDatabase db, Func, 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(configure(db.FromTypedScope(alias, noLock))); + } /// Create EXISTS predicate. public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) { @@ -17462,6 +18019,16 @@ namespace DynamORM return new TypedSqlExistsPredicate(configure(db.FromTyped(alias, noLock))); } + /// Create EXISTS predicate from scoped typed subquery factory. + public static TypedSqlPredicate ExistsScope(DynamicDatabase db, Func, 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.FromTypedScope(alias, noLock))); + } /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { diff --git a/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs index 95b21f5..b139eb8 100644 --- a/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs +++ b/DynamORM.Tests/TypedSql/TypedSqlScopeDslTests.cs @@ -146,5 +146,69 @@ namespace DynamORM.Tests.TypedSql "SELECT a.\"id_user\" AS \"root_id\", j.\"user_code\" AS \"last_code\" FROM \"sample_users\" AS a LEFT JOIN \"sample_users\" AS b ON (a.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (b.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (c.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (d.\"id_user\" = e.\"id_user\") LEFT JOIN \"sample_users\" AS f ON (e.\"id_user\" = f.\"id_user\") LEFT JOIN \"sample_users\" AS g ON (f.\"id_user\" = g.\"id_user\") LEFT JOIN \"sample_users\" AS h ON (g.\"id_user\" = h.\"id_user\") LEFT JOIN \"sample_users\" AS i ON (h.\"id_user\" = i.\"id_user\") LEFT JOIN \"sample_users\" AS j ON (i.\"id_user\" = j.\"id_user\") WHERE (j.\"user_code\" IS NOT NULL) ORDER BY j.\"user_code\" ASC", NormalizeSql(cmd.CommandText())); } + + [Test] + public void TestScopeBuilderSupportsSixteenTypedTables() + { + var cmd = Database.FromTypedScope("a") + .Join(j => j.Left().As("b").OnSql((a, b) => a.Col(x => x.Id).Eq(b.Col(x => x.Id)))) + .Join(j => j.Left().As("c").OnSql((a, b, c) => b.Col(x => x.Id).Eq(c.Col(x => x.Id)))) + .Join(j => j.Left().As("d").OnSql((a, b, c, d) => c.Col(x => x.Id).Eq(d.Col(x => x.Id)))) + .Join(j => j.Left().As("e").OnSql((a, b, c, d, e) => d.Col(x => x.Id).Eq(e.Col(x => x.Id)))) + .Join(j => j.Left().As("f").OnSql((a, b, c, d, e, f) => e.Col(x => x.Id).Eq(f.Col(x => x.Id)))) + .Join(j => j.Left().As("g").OnSql((a, b, c, d, e, f, g) => f.Col(x => x.Id).Eq(g.Col(x => x.Id)))) + .Join(j => j.Left().As("h").OnSql((a, b, c, d, e, f, g, h) => g.Col(x => x.Id).Eq(h.Col(x => x.Id)))) + .Join(j => j.Left().As("i").OnSql((a, b, c, d, e, f, g, h, i) => h.Col(x => x.Id).Eq(i.Col(x => x.Id)))) + .Join(j => j.Left().As("j").OnSql((a, b, c, d, e, f, g, h, i, j) => i.Col(x => x.Id).Eq(j.Col(x => x.Id)))) + .Join(j => j.Left().As("k").OnSql((a, b, c, d, e, f, g, h, i, j, k) => j.Col(x => x.Id).Eq(k.Col(x => x.Id)))) + .Join(j => j.Left().As("l").OnSql((a, b, c, d, e, f, g, h, i, j, k, l) => k.Col(x => x.Id).Eq(l.Col(x => x.Id)))) + .Join(j => j.Left().As("m").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m) => l.Col(x => x.Id).Eq(m.Col(x => x.Id)))) + .Join(j => j.Left().As("n").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n) => m.Col(x => x.Id).Eq(n.Col(x => x.Id)))) + .Join(j => j.Left().As("o").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) => n.Col(x => x.Id).Eq(o.Col(x => x.Id)))) + .Join(j => j.Left().As("p").OnSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => o.Col(x => x.Id).Eq(p.Col(x => x.Id)))) + .SelectSql( + (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => a.Col(x => x.Id).As("root_id"), + (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => p.Col(x => x.Code).As("tail_code")) + .WhereSql((a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) => p.Col(x => x.Code).IsNotNull()); + + Assert.AreEqual( + "SELECT a.\"id_user\" AS \"root_id\", p.\"user_code\" AS \"tail_code\" FROM \"sample_users\" AS a LEFT JOIN \"sample_users\" AS b ON (a.\"id_user\" = b.\"id_user\") LEFT JOIN \"sample_users\" AS c ON (b.\"id_user\" = c.\"id_user\") LEFT JOIN \"sample_users\" AS d ON (c.\"id_user\" = d.\"id_user\") LEFT JOIN \"sample_users\" AS e ON (d.\"id_user\" = e.\"id_user\") LEFT JOIN \"sample_users\" AS f ON (e.\"id_user\" = f.\"id_user\") LEFT JOIN \"sample_users\" AS g ON (f.\"id_user\" = g.\"id_user\") LEFT JOIN \"sample_users\" AS h ON (g.\"id_user\" = h.\"id_user\") LEFT JOIN \"sample_users\" AS i ON (h.\"id_user\" = i.\"id_user\") LEFT JOIN \"sample_users\" AS j ON (i.\"id_user\" = j.\"id_user\") LEFT JOIN \"sample_users\" AS k ON (j.\"id_user\" = k.\"id_user\") LEFT JOIN \"sample_users\" AS l ON (k.\"id_user\" = l.\"id_user\") LEFT JOIN \"sample_users\" AS m ON (l.\"id_user\" = m.\"id_user\") LEFT JOIN \"sample_users\" AS n ON (m.\"id_user\" = n.\"id_user\") LEFT JOIN \"sample_users\" AS o ON (n.\"id_user\" = o.\"id_user\") LEFT JOIN \"sample_users\" AS p ON (o.\"id_user\" = p.\"id_user\") WHERE (p.\"user_code\" IS NOT NULL)", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeSubQueryHelperSupportsScopedJoinChain() + { + var cmd = Database.FromTyped("u") + .SelectSql(u => Sql.SubQueryScope( + Database, + sq => sq + .Join(j => j.Left().As("c").OnSql((x, c) => x.Col(a => a.IdUser).Eq(c.Col(a => a.UserId)))) + .Join(j => j.Left().As("r").OnSql((x, c, r) => c.Col(a => a.UserId).Eq(r.Col(a => a.UserId)))) + .SelectSql((x, c, r) => r.Col(a => a.RoleName)), + "x").As("role_name")); + + Assert.AreEqual( + "SELECT (SELECT r.\"Role_Name\" FROM \"dbo\".\"Users\" AS x LEFT JOIN \"dbo\".\"UserClients\" AS c ON (x.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\")) AS \"role_name\" FROM \"sample_users\" AS u", + NormalizeSql(cmd.CommandText())); + } + + [Test] + public void TestScopeExistsHelperSupportsScopedJoinChain() + { + var cmd = Database.FromTyped("u") + .WhereSql(u => Sql.ExistsScope( + Database, + sq => sq + .Join(j => j.Left().As("c").OnSql((x, c) => x.Col(a => a.IdUser).Eq(c.Col(a => a.UserId)))) + .Join(j => j.Left().As("r").OnSql((x, c, r) => c.Col(a => a.UserId).Eq(r.Col(a => a.UserId)))) + .SelectSql((x, c, r) => r.Col(a => a.RoleId)), + "x")) + .SelectSql(u => u.Col(x => x.Id)); + + Assert.AreEqual( + "SELECT u.\"id_user\" FROM \"sample_users\" AS u WHERE (EXISTS (SELECT r.\"Role_Id\" FROM \"dbo\".\"Users\" AS x LEFT JOIN \"dbo\".\"UserClients\" AS c ON (x.\"Id_User\" = c.\"User_Id\") LEFT JOIN \"dbo\".\"UserRoles\" AS r ON (c.\"User_Id\" = r.\"User_Id\")))", + NormalizeSql(cmd.CommandText())); + } } } diff --git a/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs index 78e6e9c..d27fab9 100644 --- a/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs +++ b/DynamORM/Builders/IDynamicTypedSelectScopeQueryBuilder.cs @@ -101,6 +101,7 @@ namespace DynamORM.Builders public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); @@ -108,4 +109,63 @@ namespace DynamORM.Builders IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + + public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder + { + IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); + IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); + IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); + IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); + } + } \ No newline at end of file diff --git a/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs index 3e4e532..cf8f8ae 100644 --- a/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs +++ b/DynamORM/Builders/Implementation/DynamicTypedSelectScopeQueryBuilder.cs @@ -15,12 +15,7 @@ namespace DynamORM.Builders.Implementation internal abstract class DynamicTypedSelectScopeQueryBuilderBase : IDynamicSelectQueryBuilder { protected readonly DynamicTypedSelectQueryBuilder Builder; - - protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) - { - Builder = builder; - } - + protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) { Builder = builder; } public DynamicDatabase Database { get { return Builder.Database; } } public IList Tables { get { return Builder.Tables; } } public IDictionary Parameters { get { return Builder.Parameters; } } @@ -29,7 +24,6 @@ namespace DynamORM.Builders.Implementation public List> OnCreateTemporaryParameter { get { return Builder.OnCreateTemporaryParameter; } set { Builder.OnCreateTemporaryParameter = value; } } public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } public bool IsDisposed { get { return Builder.IsDisposed; } } - public void Dispose() { Builder.Dispose(); } public IDbCommand FillCommand(IDbCommand command) { return Builder.FillCommand(command); } public string CommandText() { return Builder.CommandText(); } @@ -65,13 +59,10 @@ namespace DynamORM.Builders.Implementation public IDynamicSelectQueryBuilder Limit(int? limit) { Builder.Limit(limit); return this; } public IDynamicSelectQueryBuilder Offset(int? offset) { Builder.Offset(offset); return this; } public IDynamicSelectQueryBuilder Distinct(bool distinct = true) { Builder.Distinct(distinct); return this; } - protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) { - if (!string.IsNullOrEmpty(alias)) - builder.As(alias); - if (!string.IsNullOrEmpty(customJoinType)) - builder.Type(customJoinType); + if (!string.IsNullOrEmpty(alias)) builder.As(alias); + if (!string.IsNullOrEmpty(customJoinType)) builder.Type(customJoinType); else { switch (joinType) @@ -86,13 +77,10 @@ namespace DynamORM.Builders.Implementation default: builder.Inner(); break; } } - if (noLock) - builder.NoLock(); - if (!string.IsNullOrEmpty(condition)) - builder.OnRaw(condition); + if (noLock) builder.NoLock(); + if (!string.IsNullOrEmpty(condition)) builder.OnRaw(condition); return builder; } - protected void AddSelect(TypedSqlSelectable item) { Builder.AddSelectSqlSelector(item); } protected void AddGroupBy(TypedSqlExpression item) { Builder.AddGroupBySqlSelector(item); } protected void AddOrderBy(TypedSqlOrderExpression item) { Builder.AddOrderBySqlSelector(item); } @@ -103,22 +91,16 @@ namespace DynamORM.Builders.Implementation internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) : base(builder) { _alias1 = alias1; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -127,8 +109,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) @@ -138,24 +119,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) @@ -165,23 +143,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -189,13 +165,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t3" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -204,8 +176,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) @@ -215,24 +186,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) @@ -242,14 +210,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -257,9 +225,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -268,13 +234,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t4" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -283,8 +245,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) @@ -294,24 +255,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) @@ -321,14 +279,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -337,9 +295,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -349,13 +305,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t5" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -364,8 +316,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) @@ -375,24 +326,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) @@ -402,14 +350,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -419,9 +367,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -432,13 +378,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t6" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -447,8 +389,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) @@ -458,24 +399,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) @@ -485,14 +423,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -503,9 +441,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -517,13 +453,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t7" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -532,8 +464,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) @@ -543,24 +474,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) @@ -570,14 +498,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -589,9 +517,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -604,13 +530,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t8" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -619,8 +541,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) @@ -630,24 +551,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) @@ -657,14 +575,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -677,9 +595,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -693,13 +609,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t9" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -708,8 +620,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) @@ -719,24 +630,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) @@ -746,14 +654,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -767,9 +675,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -784,13 +690,9 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { - if (specification == null) - throw new ArgumentNullException("specification"); - + if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); - if (spec == null) - throw new ArgumentException("Join specification cannot resolve to null.", "specification"); - + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t10" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); @@ -799,8 +701,7 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) @@ -810,24 +711,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) @@ -837,14 +735,14 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } + } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder @@ -859,9 +757,7 @@ namespace DynamORM.Builders.Implementation private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; - - internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) - : base(builder) + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) : base(builder) { _alias1 = alias1; _alias2 = alias2; @@ -875,10 +771,20 @@ namespace DynamORM.Builders.Implementation _alias10 = alias10; } + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t11" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, alias); + } + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) @@ -888,24 +794,21 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { - if (predicate == null) - throw new ArgumentNullException("predicate"); + if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) @@ -915,14 +818,543 @@ namespace DynamORM.Builders.Implementation public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { - if (selector == null) - throw new ArgumentNullException("selector"); + if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t12" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); + return this; + } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t13" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); + return this; + } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t14" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); + return this; + } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t15" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); + return this; + } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + private readonly string _alias15; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + _alias15 = alias15; + } + + public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) + { + if (specification == null) throw new ArgumentNullException("specification"); + TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); + if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); + string alias = string.IsNullOrEmpty(spec.Alias) ? "t16" : spec.Alias; + string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(alias))); + Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); + return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, _alias15, alias); + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); + return this; + } + + } + + internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder + { + private readonly string _alias1; + private readonly string _alias2; + private readonly string _alias3; + private readonly string _alias4; + private readonly string _alias5; + private readonly string _alias6; + private readonly string _alias7; + private readonly string _alias8; + private readonly string _alias9; + private readonly string _alias10; + private readonly string _alias11; + private readonly string _alias12; + private readonly string _alias13; + private readonly string _alias14; + private readonly string _alias15; + private readonly string _alias16; + internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15, string alias16) : base(builder) + { + _alias1 = alias1; + _alias2 = alias2; + _alias3 = alias3; + _alias4 = alias4; + _alias5 = alias5; + _alias6 = alias6; + _alias7 = alias7; + _alias8 = alias8; + _alias9 = alias9; + _alias10 = alias10; + _alias11 = alias11; + _alias12 = alias12; + _alias13 = alias13; + _alias14 = alias14; + _alias15 = alias15; + _alias16 = alias16; + } + + public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) throw new ArgumentNullException("predicate"); + AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + + public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) + { + if (selector == null) throw new ArgumentNullException("selector"); + AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + if (selectors != null) + foreach (var item in selectors) + AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); + return this; + } + } } \ No newline at end of file diff --git a/DynamORM/Builders/TypedScopeJoinBuilder.cs b/DynamORM/Builders/TypedScopeJoinBuilder.cs index 7f00084..2d1df52 100644 --- a/DynamORM/Builders/TypedScopeJoinBuilder.cs +++ b/DynamORM/Builders/TypedScopeJoinBuilder.cs @@ -22,12 +22,7 @@ namespace DynamORM.Builders JoinType = DynamicJoinType.Join; } - public TSelf As(string alias) - { - Alias = alias; - return (TSelf)this; - } - + public TSelf As(string alias) { Alias = alias; return (TSelf)this; } public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; } public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; } public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; } @@ -36,20 +31,8 @@ namespace DynamORM.Builders public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; } public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; } public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; } - - public TSelf Type(string joinType) - { - if (string.IsNullOrEmpty(joinType)) - throw new ArgumentNullException("joinType"); - CustomJoinType = joinType.Trim(); - return (TSelf)this; - } - - public TSelf NoLock(bool use = true) - { - UseNoLock = use; - return (TSelf)this; - } + public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); CustomJoinType = joinType.Trim(); return (TSelf)this; } + public TSelf NoLock(bool use = true) { UseNoLock = use; return (TSelf)this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> @@ -268,4 +251,148 @@ namespace DynamORM.Builders } } + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + + public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> + { + internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } + internal string OnRawCondition { get; private set; } + + public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + OnSqlPredicate = predicate; + OnRawCondition = null; + return this; + } + + public TypedScopeJoinBuilder OnRaw(string condition) + { + if (string.IsNullOrEmpty(condition)) + throw new ArgumentNullException("condition"); + OnRawCondition = condition.Trim(); + OnSqlPredicate = null; + return this; + } + } + } \ No newline at end of file diff --git a/DynamORM/TypedSql/Sql.cs b/DynamORM/TypedSql/Sql.cs index eea24df..06a2132 100644 --- a/DynamORM/TypedSql/Sql.cs +++ b/DynamORM/TypedSql/Sql.cs @@ -150,6 +150,17 @@ namespace DynamORM.TypedSql return new TypedSqlSubQueryExpression(configure(db.FromTyped(alias, noLock))); } + /// Create scalar typed subquery expression using the scoped typed builder API. + public static TypedSqlExpression SubQueryScope(DynamicDatabase db, Func, 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(configure(db.FromTypedScope(alias, noLock))); + } + /// Create EXISTS predicate. public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) { @@ -167,6 +178,17 @@ namespace DynamORM.TypedSql return new TypedSqlExistsPredicate(configure(db.FromTyped(alias, noLock))); } + /// Create EXISTS predicate from scoped typed subquery factory. + public static TypedSqlPredicate ExistsScope(DynamicDatabase db, Func, 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.FromTypedScope(alias, noLock))); + } + /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { From 0afc894fd6f3e0815f9f476c21de95d8bfccbfc7 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 13:34:16 +0100 Subject: [PATCH 18/20] Add docs for typed fluent syntax --- docs/README.md | 3 + docs/quick-start.md | 16 ++ docs/typed-fluent-syntax.md | 367 ++++++++++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 docs/typed-fluent-syntax.md diff --git a/docs/README.md b/docs/README.md index 025deea..fd7fe6c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,7 @@ This documentation is based on: - [Quick Start](quick-start.md) - [Dynamic Table API](dynamic-table-api.md) - [Fluent Builder API](fluent-builder-api.md) +- [Typed Fluent Syntax](typed-fluent-syntax.md) - [Mapping and Entities](mapping-and-entities.md) - [Transactions and Disposal](transactions-and-disposal.md) - [Stored Procedures](stored-procedures.md) @@ -36,3 +37,5 @@ DynamORM provides two major usage styles: - `db.Insert() / db.Update() / db.Delete()` The dynamic API is concise and fast to use. The fluent builder API gives stronger composition and explicit SQL control. + +For the newer property-mapped typed fluent syntax, see [Typed Fluent Syntax](typed-fluent-syntax.md). diff --git a/docs/quick-start.md b/docs/quick-start.md index 4a24707..9f3b3d4 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -52,6 +52,22 @@ using (var query = db.From("users").Where("id", 19).SelectColumn("first")) } ``` +## First Query (Typed Fluent Syntax) + +```csharp +using (var db = new DynamicDatabase(SQLiteFactory.Instance, "Data Source=app.db;", options)) +{ + var cmd = db.FromTyped("u") + .SelectSql(u => u.Col(x => x.Code)) + .WhereSql(u => u.Col(x => x.Id).Eq(19)); + + var value = cmd.ScalarAs(); + Console.WriteLine(value); +} +``` + +For multi-join typed queries, prefer `FromTypedScope(...)`. Full details are documented in [Typed Fluent Syntax](typed-fluent-syntax.md). + ## Insert, Update, Delete ```csharp diff --git a/docs/typed-fluent-syntax.md b/docs/typed-fluent-syntax.md new file mode 100644 index 0000000..e8b4968 --- /dev/null +++ b/docs/typed-fluent-syntax.md @@ -0,0 +1,367 @@ +# Typed Fluent Syntax + +This page documents the new typed fluent syntax added alongside the original dynamic fluent builder API. + +It is intended for cases where: + +- you want property-based IntelliSense instead of raw column names +- you want mapped property validation in fluent SQL construction +- you still want explicit SQL composition instead of LINQ translation + +The old dynamic fluent API remains unchanged. This typed API is additive. + +## Two Typed Styles + +There are currently two typed select styles: + +- positional typed builder: + - `db.FromTyped("alias")` +- scoped typed builder: + - `db.FromTypedScope("alias")` + +Recommendation: + +- use `FromTypedScope(...)` for multi-join queries +- use `FromTyped(...)` for simple root-table typed queries or when you want direct positional generic overloads + +## Root Typed Builder + +Basic example: + +```csharp +var cmd = db.FromTyped("u") + .SelectSql(u => u.Col(x => x.Code)) + .WhereSql(u => u.Col(x => x.Id).Eq(1)); +``` + +Mapped properties are resolved through entity metadata: + +```csharp +[Table(Name = "sample_users")] +public class User +{ + [Column("id_user", true)] + public long Id { get; set; } + + [Column("user_code")] + public string Code { get; set; } +} +``` + +So: + +- `u.Col(x => x.Id)` renders mapped column `u."id_user"` +- `u.Col(x => x.Code)` renders mapped column `u."user_code"` + +## Core Building Blocks + +The typed DSL is built around: + +- `TypedTableContext` +- `TypedSqlExpression` +- `TypedSqlPredicate` +- `TypedSqlOrderExpression` +- `Sql.*` helpers + +Common building blocks: + +```csharp +u.Col(x => x.Id) +Sql.Val(1) +Sql.Count() +Sql.Coalesce(u.Col(x => x.Code), Sql.Val("N/A")) +Sql.Case().When(...).Else(...) +``` + +## Positional Typed Builder + +`FromTyped(...)` supports typed joins and then exposes joined tables by position. + +Example: + +```csharp +var cmd = db.FromTyped("u") + .Join(j => j.Left().As("c").OnSql((u, c) => + u.Col(x => x.Id).Eq(c.Col(x => x.UserId)))) + .SelectSql( + (u, c) => u.Col(x => x.Id).As("user_id"), + (u, c) => c.Col(x => x.ClientCode).As("client_code")) + .WhereSql((u, c) => + c.Col(x => x.IsDeleted).Eq(0)); +``` + +This style is explicit, but becomes noisy as join count grows because join types must be repeated in generic parameters. + +## Scoped Typed Builder + +`FromTypedScope(...)` solves the repeated-generic problem by changing builder type after each join. + +Example: + +```csharp +var cmd = db.FromTypedScope("u") + .Join(j => j.Left().As("c").OnSql((u, c) => + u.Col(x => x.Id).Eq(c.Col(x => x.UserId)))) + .Join(j => j.Left().As("r").OnSql((u, c, r) => + c.Col(x => x.UserId).Eq(r.Col(x => x.UserId)))) + .SelectSql( + (u, c, r) => u.Col(x => x.Id), + (u, c, r) => r.Col(x => x.RoleName).As("role_name")) + .WhereSql((u, c, r) => + r.Col(x => x.RoleId).Gt(0)); +``` + +Important behavior: + +- each `Join(...)` extends the available typed lambda parameters +- later joins can reference previously joined tables inside `OnSql(...)` +- this is the preferred typed syntax for larger join graphs + +Current implementation supports up to `16` typed tables in one scoped chain. + +## Join Syntax + +Typed joins support: + +- `Inner()` +- `Join()` +- `Left()` +- `Right()` +- `Full()` +- `LeftOuter()` +- `RightOuter()` +- `FullOuter()` +- `Type("...")` for custom join keywords +- `NoLock()` +- `OnSql(...)` +- `OnRaw(...)` + +Examples: + +```csharp +.Join(j => j.Left().As("c").OnSql((u, c) => + u.Col(x => x.Id).Eq(c.Col(x => x.UserId)))) + +.Join(j => j.Type("CROSS APPLY").As("c").NoLock()) + +.Join(j => j.Left().As("r").OnRaw("c.\"User_Id\" = r.\"User_Id\"")) +``` + +`OnSql(...)` is usually the best option because it stays mapped and typed. + +## Selecting Columns + +Single-column select: + +```csharp +.SelectSql(u => u.Col(x => x.Code)) +``` + +Aliased select: + +```csharp +.SelectSql(u => u.Col(x => x.Code).As("user_code")) +``` + +Wildcard: + +```csharp +.SelectSql(u => u.All()) +``` + +Multi-table scoped select: + +```csharp +.SelectSql( + (u, c, r) => u.Col(x => x.Id).As("user_id"), + (u, c, r) => c.Col(x => x.ClientCode).As("client_code"), + (u, c, r) => r.Col(x => x.RoleName).As("role_name")) +``` + +## Predicates + +Available predicate patterns include: + +- `Eq` +- `NotEq` +- `Gt` +- `Gte` +- `Lt` +- `Lte` +- `IsNull` +- `IsNotNull` +- `Like` +- `StartsWith` +- `EndsWith` +- `Contains` +- `In` +- `NotIn` +- `Between` +- `And` +- `Or` +- `Not` + +Example: + +```csharp +.WhereSql((u, c) => + u.Col(x => x.Code).StartsWith("ADM") + .And(c.Col(x => x.IsDeleted).Eq(0)) + .And(c.Col(x => x.ClientCode).IsNotNull())) +``` + +## Functions and Expressions + +Standard helpers: + +- `Sql.Count()` +- `Sql.Count(expr)` +- `Sql.Sum(...)` +- `Sql.Avg(...)` +- `Sql.Min(...)` +- `Sql.Max(...)` +- `Sql.Abs(...)` +- `Sql.Upper(...)` +- `Sql.Lower(...)` +- `Sql.Trim(...)` +- `Sql.Length(...)` +- `Sql.NullIf(...)` +- `Sql.Coalesce(...)` +- `Sql.CurrentTimestamp()` + +Examples: + +```csharp +.SelectSql( + u => Sql.Count().As("cnt"), + u => Sql.Coalesce(u.Col(x => x.Code), Sql.Val("N/A")).As("code_value")) + +.HavingSql((u, c) => Sql.Count(c.Col(x => x.UserId)).Gt(0)) +``` + +Arithmetic is also supported: + +```csharp +.SelectSql(u => (u.Col(x => x.Id) + Sql.Val(1)).As("plus_one")) +``` + +## CASE Expressions + +```csharp +.SelectSql(u => Sql.Case() + .When(u.Col(x => x.Code).Eq("A"), "Alpha") + .When(u.Col(x => x.Code).Eq("B"), "Beta") + .Else("Other") + .As("category")) +``` + +Use `CASE` when expression logic becomes too awkward for simple `COALESCE` or comparison predicates. + +## Grouping and Ordering + +Grouping: + +```csharp +.GroupBySql((u, c) => u.Col(x => x.Id)) +``` + +Ordering: + +```csharp +.OrderBySql( + (u, c) => c.Col(x => x.ClientCode).Asc(), + (u, c) => Sql.Count(c.Col(x => x.UserId)).Desc()) +``` + +Null ordering helpers are also available: + +- `NullsFirst()` +- `NullsLast()` + +Raw order fragments: + +```csharp +.OrderBySql(u => Sql.RawOrder("2 DESC")) +``` + +## Custom SQL Escape Hatches + +For cases not covered by built-in helpers: + +- `Sql.Raw(...)` +- `Sql.Func(...)` +- `OnRaw(...)` + +Examples: + +```csharp +Sql.Raw("ROW_NUMBER() OVER(ORDER BY u.\"id_user\")") +Sql.Func("CUSTOM_FUNC", u.Col(x => x.Code), Sql.Val(5)) +``` + +Use these when you need provider-specific or advanced SQL fragments, but keep normal query construction on typed helpers where possible. + +## Scoped Subqueries + +Two subquery helper families exist: + +- classic typed helper: + - `Sql.SubQuery(...)` + - `Sql.Exists(...)` +- scoped typed helper: + - `Sql.SubQueryScope(...)` + - `Sql.ExistsScope(...)` + +Use the scoped helpers when the subquery itself benefits from `FromTypedScope(...)`. + +Example: + +```csharp +.SelectSql(u => Sql.SubQueryScope( + db, + sq => sq + .Join(j => j.Left().As("c").OnSql((x, c) => + x.Col(a => a.Id).Eq(c.Col(a => a.UserId)))) + .Join(j => j.Left().As("r").OnSql((x, c, r) => + c.Col(a => a.UserId).Eq(r.Col(a => a.UserId)))) + .SelectSql((x, c, r) => r.Col(a => a.RoleName)), + "x").As("role_name")) +``` + +`ExistsScope(...)` works the same way, but returns a predicate. + +## Modify Builders + +Typed SQL DSL also exists for modify builders: + +- `InsertTyped()` +- `UpdateTyped()` +- `DeleteTyped()` + +Examples: + +```csharp +db.UpdateTyped() + .SetSql(u => u.Code, u => Sql.Coalesce(Sql.Val("900"), u.Col(x => x.Code))) + .WhereSql(u => u.Col(x => x.Id).Eq(1)); + +db.InsertTyped() + .InsertSql(u => u.Code, u => Sql.Val("901")); + +db.DeleteTyped() + .WhereSql(u => u.Col(x => x.Id).Eq(2)); +``` + +## Practical Guidance + +- prefer `FromTypedScope(...)` once a query has more than one or two joins +- use `OnSql(...)` instead of `OnRaw(...)` whenever mappings exist +- use `Sql.Func(...)` and `Sql.Raw(...)` sparingly as escape hatches +- use `SubQueryScope(...)` / `ExistsScope(...)` when the subquery itself is multi-join and typed +- keep the original dynamic fluent builder for cases where fully dynamic alias/member construction is still the better fit + +## Related Pages + +- [Fluent Builder API](fluent-builder-api.md) +- [Mapping and Entities](mapping-and-entities.md) +- [Test-Driven Examples](test-driven-examples.md) From 3c8ad870924aab878ea2cc11e373110f855bd494 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 13:35:14 +0100 Subject: [PATCH 19/20] Link typed fluent docs from fluent builder docs --- docs/fluent-builder-api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/fluent-builder-api.md b/docs/fluent-builder-api.md index 55449eb..c84aba1 100644 --- a/docs/fluent-builder-api.md +++ b/docs/fluent-builder-api.md @@ -1,5 +1,7 @@ # Fluent Builder API +For the newer property-mapped typed fluent API, see [Typed Fluent Syntax](typed-fluent-syntax.md). This page focuses on the original dynamic fluent builder semantics and parser behavior. + The fluent API is built around: - `IDynamicSelectQueryBuilder` From 454a2826599e068c93eb033cbe4b8c8a1c645e69 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 13:43:50 +0100 Subject: [PATCH 20/20] Bump VersionPrefix to 2.0 --- DynamORM.Net40.csproj | 2 +- DynamORM.Tests/DynamORM.Tests.csproj | 2 +- DynamORM/DynamORM.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DynamORM.Net40.csproj b/DynamORM.Net40.csproj index d84347f..47bb223 100644 --- a/DynamORM.Net40.csproj +++ b/DynamORM.Net40.csproj @@ -5,7 +5,7 @@ Copyright © RUSSEK Software 2012-2026 RUSSEK Software Grzegorz Russek - 1.9 + 2.0 DynamORM MIT false diff --git a/DynamORM.Tests/DynamORM.Tests.csproj b/DynamORM.Tests/DynamORM.Tests.csproj index 428d6ba..bd146fa 100644 --- a/DynamORM.Tests/DynamORM.Tests.csproj +++ b/DynamORM.Tests/DynamORM.Tests.csproj @@ -6,7 +6,7 @@ Copyright © RUSSEK Software 2012-2026 RUSSEK Software Grzegorz Russek - 1.8 + 2.0 https://git.dr4cul4.pl/RUSSEK-Software/DynamORM https://dr4cul4.pl DynamORM diff --git a/DynamORM/DynamORM.csproj b/DynamORM/DynamORM.csproj index 103f8ef..1a8738a 100644 --- a/DynamORM/DynamORM.csproj +++ b/DynamORM/DynamORM.csproj @@ -6,7 +6,7 @@ Copyright © RUSSEK Software 2012-2026 RUSSEK Software Grzegorz Russek - 1.9 + 2.0 https://git.dr4cul4.pl/RUSSEK-Software/DynamORM https://dr4cul4.pl DynamORM