From 8555e1958c3e205c6642f4621e58c506446187d8 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 09:29:23 +0100 Subject: [PATCH] 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.