Skip to content

Commit 4e724d4

Browse files
authored
Merge pull request #55 from PandaTechAM/development
ColumnDistinctValue Encryption Handling Upgrade
2 parents e6be013 + c0f5417 commit 4e724d4

File tree

3 files changed

+43
-54
lines changed

3 files changed

+43
-54
lines changed

src/GridifyExtensions/DbContextFunction/PostgresFunctions.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/GridifyExtensions/Extensions/QueryableExtensions.cs

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -163,25 +163,34 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
163163
.Distinct()
164164
.OrderBy(x => x)
165165
.GetPagedAsync(model, cancellationToken);
166-
167166
return result;
168167
}
169168

170-
var item = await query
171-
.ApplyFiltering(model, mapper)
172-
.Select(CreateSelector<TEntity>(model.PropertyName))
173-
.FirstOrDefaultAsync(cancellationToken);
169+
// Encrypted path (scalar byte[] or IEnumerable<byte[]>), use mapper-driven selection
170+
var encryptedQuery = query
171+
.ApplyFiltering(model, mapper)
172+
.ApplySelect(model.PropertyName, mapper); // IQueryable<object?>
174173

175-
if (item is null || string.IsNullOrEmpty(model.Filter))
174+
// Keep original behavior: if no filter, return empty for the obsolete API
175+
if (string.IsNullOrWhiteSpace(model.Filter))
176+
{
176177
return new PagedResponse<object>([], 1, 1, 0);
178+
}
177179

178-
if (decryptor is null)
180+
var selected = await encryptedQuery.FirstOrDefaultAsync(cancellationToken);
181+
if (selected is null) return new PagedResponse<object>([], 1, 1, 0);
182+
if (decryptor is null) throw new KeyNotFoundException("Decryptor is required for encrypted properties.");
183+
184+
object? decrypted = selected switch
179185
{
180-
throw new KeyNotFoundException("Decryptor is required for encrypted properties.");
181-
}
186+
byte[] b => decryptor(b),
187+
IEnumerable<byte[]> bs => bs.FirstOrDefault() is byte[] fb ? decryptor(fb) : null,
188+
_ => throw new InvalidCastException("Encrypted selector did not return a byte[] or IEnumerable<byte[]> value.")
189+
};
182190

183-
var decrypted = decryptor((byte[])item);
184-
return new PagedResponse<object>([decrypted], 1, 1, 1);
191+
return decrypted is null
192+
? new PagedResponse<object>([], 1, 1, 0)
193+
: new PagedResponse<object>([decrypted], 1, 1, 1);
185194
}
186195

187196
public static async Task<CursoredResponse<object?>> ColumnDistinctValuesAsync<TEntity>(
@@ -208,10 +217,7 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
208217
{
209218
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
210219
hasNull = await baseQuery.AnyAsync(x => x == null, cancellationToken);
211-
if (hasNull && take > 0)
212-
{
213-
take -= 1;
214-
}
220+
if (hasNull && take > 0) take -= 1;
215221
}
216222

217223
var result = await baseQuery
@@ -221,38 +227,33 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
221227
.ToListAsync(cancellationToken);
222228

223229
if (!filterEmpty || !hasNull)
224-
{
225230
return new CursoredResponse<object?>(result!, model.PageSize);
226-
}
227231

228232
if (result.Count > 0 && ReferenceEquals(result[^1], null))
229-
{
230233
result.RemoveAt(result.Count - 1);
231-
}
232234

233235
result.Insert(0, null!);
234236
return new CursoredResponse<object?>(result!, model.PageSize);
235237
}
236238

237-
// Encrypted (byte[]) path:
239+
// Encrypted path (scalar byte[] or IEnumerable<byte[]>)
238240
var encryptedQuery = query
239241
.ApplyFiltering(gridifyModel, mapper)
240-
.Select(CreateSelector<TEntity>(model.PropertyName));
242+
.ApplySelect(model.PropertyName, mapper); // IQueryable<object?>
241243

242244
if (string.IsNullOrWhiteSpace(model.Filter))
243245
{
246+
// EF-translatable: only checks if the projection itself is NULL in DB
244247
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
245248
var hasNull = await encryptedQuery.AnyAsync(x => x == null, cancellationToken);
246249

247-
if (hasNull)
248-
return new CursoredResponse<object?>([null],
249-
model.PageSize);
250-
251-
return new CursoredResponse<object?>([], model.PageSize);
250+
return hasNull
251+
? new CursoredResponse<object?>([null], model.PageSize)
252+
: new CursoredResponse<object?>([], model.PageSize);
252253
}
253254

254-
var item = await encryptedQuery.FirstOrDefaultAsync(cancellationToken);
255-
if (item is null)
255+
var selected = await encryptedQuery.FirstOrDefaultAsync(cancellationToken);
256+
if (selected is null)
256257
{
257258
return new CursoredResponse<object?>([], model.PageSize);
258259
}
@@ -262,13 +263,19 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
262263
throw new KeyNotFoundException("Decryptor is required for encrypted properties.");
263264
}
264265

265-
if (item is not byte[] bytes)
266+
object? decrypted = selected switch
266267
{
267-
throw new InvalidCastException("Encrypted selector did not return a byte[] value.");
268-
}
268+
byte[] b => decryptor(b),
269+
IEnumerable<byte[]> bs => bs.FirstOrDefault() is
270+
{ } fb
271+
? decryptor(fb)
272+
: null,
273+
_ => throw new InvalidCastException("Encrypted selector did not return a byte[] or IEnumerable<byte[]> value.")
274+
};
269275

270-
var decrypted = decryptor(bytes);
271-
return new CursoredResponse<object?>([decrypted], model.PageSize);
276+
return decrypted is null
277+
? new CursoredResponse<object?>([], model.PageSize)
278+
: new CursoredResponse<object?>([decrypted], model.PageSize);
272279
}
273280

274281
// ---------- Aggregation ----------

src/GridifyExtensions/GridifyExtensions.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>2.1.2</Version>
11+
<Version>2.1.3</Version>
1212
<PackageId>Pandatech.GridifyExtensions</PackageId>
1313
<Title>Pandatech.Gridify.Extensions</Title>
1414
<PackageTags>Pandatech, library, Gridify, Pagination, Filters</PackageTags>
1515
<Description>Pandatech.Gridify.Extensions simplifies and extends the functionality of the Gridify NuGet package. It provides additional extension methods and functionality to streamline data filtering and pagination, making it more intuitive and powerful to use in .NET applications. Our enhancements ensure more flexibility, reduce boilerplate code, and improve overall developer productivity when working with Gridify.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-gridify-extensions</RepositoryUrl>
17-
<PackageReleaseNotes>ColumnDistinctValue null handling extended</PackageReleaseNotes>
17+
<PackageReleaseNotes>ColumnDistinctValue Encryption Handling Upgrade</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
@@ -23,7 +23,7 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26-
<PackageReference Include="Gridify.EntityFramework" Version="2.16.3" />
26+
<PackageReference Include="Gridify.EntityFramework" Version="2.17.0" />
2727
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
2828
</ItemGroup>
2929

0 commit comments

Comments
 (0)