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;