1- using System . Collections ;
1+ using System . Collections ;
2+ using System . Collections . Frozen ;
23using System . Linq . Expressions ;
34using System . Text . RegularExpressions ;
45using Gridify ;
@@ -15,7 +16,7 @@ namespace GridifyExtensions.Extensions;
1516/// </summary>
1617public static class QueryableExtensions
1718{
18- internal static Dictionary < Type , object > EntityGridifyMapperByType = [ ] ;
19+ internal static FrozenDictionary < Type , object > EntityGridifyMapperByType = FrozenDictionary < Type , object > . Empty ;
1920
2021 // ---------- Core helpers ----------
2122 private static FilterMapper < TEntity > RequireMapper < TEntity > ( )
@@ -172,85 +173,6 @@ public static Task<CursoredResponse<TEntity>> FilterOrderAndGetCursoredAsync<TEn
172173 . FilterOrderAndGetCursoredAsync ( model , x => x , ct ) ;
173174 }
174175
175- /// <summary>
176- /// Get distinct values for a column (obsolete - use cursored version).
177- /// </summary>
178- [ Obsolete ( "Use ColumnDistinctValueCursoredQueryModel instead." ) ]
179- public static async Task < PagedResponse < object > > ColumnDistinctValuesAsync < TEntity > ( this IQueryable < TEntity > query ,
180- ColumnDistinctValueQueryModel model ,
181- Func < byte [ ] , string > ? decryptor = null ,
182- CancellationToken ct = default )
183- where TEntity : class
184- {
185- var mapper = RequireMapper < TEntity > ( ) ;
186-
187- if ( ! mapper . IsEncrypted ( model . PropertyName ) )
188- {
189- var result = await query
190- . ApplyFiltering ( model , mapper )
191- . ApplySelect ( model . PropertyName , mapper )
192- . Distinct ( )
193- . OrderBy ( x => x )
194- . GetPagedAsync ( model , ct ) ;
195- return result ;
196- }
197-
198- var encryptedQuery = query
199- . ApplyFiltering ( model , mapper )
200- . ApplySelect ( model . PropertyName , mapper ) ;
201-
202- if ( string . IsNullOrWhiteSpace ( model . Filter ) )
203- {
204- bool hasNullLike ;
205- try
206- {
207- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
208- hasNullLike = await encryptedQuery . AnyAsync ( x => x == null , ct ) ;
209- }
210- catch ( Exception ex ) when ( ex is InvalidOperationException or NotSupportedException )
211- {
212- hasNullLike = true ;
213- }
214-
215- return hasNullLike ? new PagedResponse < object > ( [ null ! ] , 1 , 1 , 1 ) : new PagedResponse < object > ( [ ] , 1 , 1 , 0 ) ;
216- }
217-
218- var selected = await encryptedQuery . FirstOrDefaultAsync ( ct ) ;
219- switch ( selected )
220- {
221- case null :
222- case byte [ ] { Length : 0 } :
223- return new PagedResponse < object > ( [ null ! ] , 1 , 1 , 1 ) ;
224- case byte [ ] sb :
225- return decryptor == null
226- ? throw new KeyNotFoundException ( "Decryptor is required for encrypted properties." )
227- : new PagedResponse < object > ( [ decryptor ( sb ) ] , 1 , 1 , 1 ) ;
228- }
229-
230- if ( selected is not IEnumerable < byte [ ] > seq )
231- {
232- throw new InvalidCastException ( "Encrypted selector did not return a byte[] or IEnumerable<byte[]> value." ) ;
233- }
234-
235- var ng = ( ( IEnumerable ) seq ) . GetEnumerator ( ) ;
236- using var ng1 = ng as IDisposable ;
237-
238- if ( ! ng . MoveNext ( ) )
239- {
240- return new PagedResponse < object > ( [ null ! ] , 1 , 1 , 1 ) ;
241- }
242-
243- var firstObj = ng . Current ;
244- if ( firstObj is not byte [ ] first || first . Length == 0 )
245- {
246- return new PagedResponse < object > ( [ null ! ] , 1 , 1 , 1 ) ;
247- }
248-
249- return decryptor == null
250- ? throw new KeyNotFoundException ( "Decryptor is required for encrypted properties." )
251- : new PagedResponse < object > ( [ decryptor ( first ) ] , 1 , 1 , 1 ) ;
252- }
253-
254176 /// <summary>
255177 /// Get distinct values for a column with cursor pagination.
256178 /// </summary>
@@ -290,7 +212,7 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
290212 . ToListAsync ( ct ) ;
291213
292214 return new CursoredResponse < object ? > ( data . Cast < object ? > ( )
293- . ToList ( ) ,
215+ . ToList ( ) ,
294216 model . PageSize ) ;
295217 }
296218
@@ -369,7 +291,7 @@ public static async Task<object> AggregateAsync<TEntity>(this IQueryable<TEntity
369291 where TEntity : class
370292 {
371293 var mapper = RequireMapper < TEntity > ( ) ;
372- var filtered = query . ApplyFiltering ( model , mapper )
294+ var filtered = query . ApplyFiltering ( model . ToGridifyQueryModel ( ) , mapper )
373295 . ApplySelect ( model . PropertyName , mapper ) ;
374296
375297 return model . AggregateType switch
@@ -392,19 +314,19 @@ public static IEnumerable<MappingModel> GetMappings<TEntity>()
392314 var mapper = EntityGridifyMapperByType [ typeof ( TEntity ) ] as FilterMapper < TEntity > ;
393315
394316 return mapper ! . GetCurrentMaps ( )
395- . Select ( x => new MappingModel
396- {
397- Name = x . From ,
398- Type = x . To . Body switch
317+ . Select ( x => new MappingModel (
318+ x . From ,
319+ x . To . Body switch
399320 {
400321 UnaryExpression ue => ue . Operand . Type . Name ,
401322 MethodCallExpression mc => ( mc . Arguments . LastOrDefault ( ) as LambdaExpression ) ? . ReturnType . Name
402323 ?? x . To . Body . Type . Name ,
403324 _ => x . To . Body . Type . Name
404- }
405- } ) ;
325+ } ) ) ;
406326 }
407327
328+ // ---------- Private helpers ----------
329+
408330 private static Expression < Func < TEntity , string ? > > EfStringSelector < TEntity > ( string propertyName )
409331 where TEntity : class
410332 {
@@ -499,4 +421,4 @@ private static bool IsStringColumn<TEntity>(IQueryable<TEntity> query, FilterMap
499421 return infra . Instance . GetService < ICurrentDbContext > ( )
500422 ? . Context ;
501423 }
502- }
424+ }
0 commit comments