1+ using System . Linq . Expressions ;
2+ using System . Text ;
3+
4+ namespace CmdScale . EntityFrameworkCore . TimescaleDB . Configuration . Hypertable
5+ {
6+ /// <summary>
7+ /// Represents an ordering specification for a column.
8+ /// </summary>
9+ /// <param name="columnName">The name of the column to order by.</param>
10+ /// <param name="isAscending">
11+ /// If true, orders Ascending (ASC).
12+ /// If false, orders Descending (DESC).
13+ /// If null, uses database default (ASC).
14+ /// </param>
15+ /// <param name="nullsFirst">
16+ /// If true, forces NULLS FIRST.
17+ /// If false, forces NULLS LAST.
18+ /// If null, uses database default (NULLS LAST for ASC, NULLS FIRST for DESC).
19+ /// </param>
20+ public class OrderBy ( string columnName , bool ? isAscending = null , bool ? nullsFirst = null )
21+ {
22+ /// <summary>The name of the column to order by.</summary>
23+ public string ColumnName { get ; } = columnName ;
24+
25+ /// <summary>Ordering direction. True for ASC, false for DESC, null for database default.</summary>
26+ public bool ? IsAscending { get ; } = isAscending ;
27+
28+ /// <summary>Null sorting behavior. True for NULLS FIRST, false for NULLS LAST, null for database default.</summary>
29+ public bool ? NullsFirst { get ; } = nullsFirst ;
30+
31+ /// <summary>
32+ /// Converts this ordering specification to a SQL clause fragment.
33+ /// </summary>
34+ public string ToSql ( )
35+ {
36+ StringBuilder sb = new ( ColumnName ) ;
37+
38+ // Only append direction if explicitly set
39+ if ( IsAscending . HasValue )
40+ {
41+ sb . Append ( IsAscending . Value ? " ASC" : " DESC" ) ;
42+ }
43+
44+ // Only append NULLS clause if explicitly set
45+ if ( NullsFirst . HasValue )
46+ {
47+ sb . Append ( NullsFirst . Value ? " NULLS FIRST" : " NULLS LAST" ) ;
48+ }
49+
50+ return sb . ToString ( ) ;
51+ }
52+ }
53+
54+ /// <summary>
55+ /// Fluent builder for creating OrderBy instances.
56+ /// </summary>
57+ public static class OrderByBuilder
58+ {
59+ /// <summary>
60+ /// Starts building an OrderBy specification for the specified property.
61+ /// </summary>
62+ /// <typeparam name="TEntity">The entity type containing the property.</typeparam>
63+ /// <param name="expression">A lambda expression selecting the property to order by.</param>
64+ public static OrderByConfiguration < TEntity > For < TEntity > ( Expression < Func < TEntity , object > > expression ) => new ( expression ) ;
65+ }
66+
67+ /// <summary>
68+ /// Fluent configuration for creating OrderBy instances.
69+ /// </summary>
70+ public class OrderByConfiguration < TEntity > ( Expression < Func < TEntity , object > > expression )
71+ {
72+ private readonly string _propertyName = GetPropertyName ( expression ) ;
73+
74+ /// <summary>Creates an OrderBy using the database default direction.</summary>
75+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
76+ public OrderBy Default ( bool ? nullsFirst = null ) => new ( _propertyName , null , nullsFirst ) ;
77+
78+ /// <summary>Creates an ascending OrderBy specification.</summary>
79+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
80+ public OrderBy Ascending ( bool ? nullsFirst = null ) => new ( _propertyName , true , nullsFirst ) ;
81+
82+ /// <summary>Creates a descending OrderBy specification.</summary>
83+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
84+ public OrderBy Descending ( bool ? nullsFirst = null ) => new ( _propertyName , false , nullsFirst ) ;
85+
86+ // Helper to extract the string name from the expression
87+ private static string GetPropertyName ( Expression < Func < TEntity , object > > expression )
88+ {
89+ if ( expression . Body is MemberExpression member ) return member . Member . Name ;
90+ if ( expression . Body is UnaryExpression unary && unary . Operand is MemberExpression m ) return m . Member . Name ;
91+ throw new ArgumentException ( "Invalid expression. Please use a simple property access expression." ) ;
92+ }
93+ }
94+
95+ /// <summary>
96+ /// Fluent builder for creating OrderBy instances using lambda expressions.
97+ /// </summary>
98+ /// <typeparam name="TEntity"></typeparam>
99+ public class OrderBySelector < TEntity >
100+ {
101+ /// <summary>Creates an OrderBy using the database default direction for the selected property.</summary>
102+ /// <param name="expression">A lambda expression selecting the property to order by.</param>
103+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
104+ public OrderBy By ( Expression < Func < TEntity , object > > expression , bool ? nullsFirst = null )
105+ => new ( GetPropertyName ( expression ) , null , nullsFirst ) ;
106+
107+ /// <summary>Creates an ascending OrderBy specification for the selected property.</summary>
108+ /// <param name="expression">A lambda expression selecting the property to order by.</param>
109+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
110+ public OrderBy ByAscending ( Expression < Func < TEntity , object > > expression , bool ? nullsFirst = null )
111+ => new ( GetPropertyName ( expression ) , true , nullsFirst ) ;
112+
113+ /// <summary>Creates a descending OrderBy specification for the selected property.</summary>
114+ /// <param name="expression">A lambda expression selecting the property to order by.</param>
115+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
116+ public OrderBy ByDescending ( Expression < Func < TEntity , object > > expression , bool ? nullsFirst = null )
117+ => new ( GetPropertyName ( expression ) , false , nullsFirst ) ;
118+
119+ private static string GetPropertyName ( Expression < Func < TEntity , object > > expression )
120+ {
121+ if ( expression . Body is MemberExpression m ) return m . Member . Name ;
122+ if ( expression . Body is UnaryExpression u && u . Operand is MemberExpression m2 ) return m2 . Member . Name ;
123+ throw new ArgumentException ( "Expression must be a property access." ) ;
124+ }
125+ }
126+
127+ /// <summary>
128+ /// Extension methods for creating OrderBy instances.
129+ /// </summary>
130+ public static class OrderByExtensions
131+ {
132+ /// <summary>
133+ /// Creates an ascending OrderBy instance.
134+ /// </summary>
135+ /// <param name="columnName">The name of the column to order by.</param>
136+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
137+ public static OrderBy Ascending ( this string columnName , bool ? nullsFirst = null )
138+ {
139+ return new OrderBy ( columnName , true , nullsFirst ) ;
140+ }
141+
142+ /// <summary>
143+ /// Creates a descending OrderBy instance.
144+ /// </summary>
145+ /// <param name="columnName">The name of the column to order by.</param>
146+ /// <param name="nullsFirst">Optional null sorting behavior. Null uses database default.</param>
147+ public static OrderBy Descending ( this string columnName , bool ? nullsFirst = null )
148+ {
149+ return new OrderBy ( columnName , false , nullsFirst ) ;
150+ }
151+ }
152+ }
0 commit comments