Skip to content

Commit 674b3c7

Browse files
committed
Merge remote-tracking branch 'origin/development' into development
2 parents 8551866 + d8a2999 commit 674b3c7

File tree

7 files changed

+145
-31
lines changed

7 files changed

+145
-31
lines changed

Readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ builder.AddGridify(params Assembly[] assemblies);
3535

3636
## Usage
3737
**Creating Mappings for Your Entities:**
38-
To efficiently filter and query your Book entity using Gridify, you need to create a mapping class that extends GridifyMapper<T>.
38+
To efficiently filter and query your Book entity using Gridify, you need to create a mapping class that extends FilterMapper<T>.
3939
This class will define how each property in your Book entity should be mapped.
4040

4141
Here’s an example of how to set up the Book entity and its corresponding mapping class:
@@ -52,7 +52,7 @@ public class Book
5252
public Book OtherBook { get; set; }
5353
}
5454

55-
public class BookMapper : GridifyMapper<Book>
55+
public class BookMapper : FilterMapper<Book>
5656
{
5757
public BookMapper()
5858
{
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace GridifyExtensions.Abstractions;
2+
3+
public interface IOrderThenBy
4+
{
5+
IOrderThenBy ThenBy(string column);
6+
IOrderThenBy ThenByDescending(string column);
7+
}

src/GridifyExtensions/Extensions/QueryableExtensions.cs

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using GridifyExtensions.Enums;
33
using GridifyExtensions.Models;
44
using Microsoft.EntityFrameworkCore;
5+
using System.Collections;
56
using System.Linq.Expressions;
67

78
namespace GridifyExtensions.Extensions;
@@ -15,7 +16,9 @@ public static async Task<PagedResponse<TDto>> FilterOrderAndGetPagedAsync<TEntit
1516
Expression<Func<TEntity, TDto>> selectExpression, CancellationToken cancellationToken = default)
1617
where TEntity : class
1718
{
18-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
19+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
20+
21+
model.OrderBy ??= mapper!.GetDefaultOrderExpression();
1922

2023
query = query.ApplyFilteringAndOrdering(model, mapper);
2124

@@ -37,15 +40,17 @@ public static Task<PagedResponse<TEntity>> FilterOrderAndGetPagedAsync<TEntity>(
3740
public static IQueryable<TEntity> ApplyFilter<TEntity>(this IQueryable<TEntity> query, GridifyQueryModel model)
3841
where TEntity : class
3942
{
40-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
43+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
4144

4245
return query.AsNoTracking().ApplyFiltering(model, mapper);
4346
}
4447

4548
public static IQueryable<TEntity> ApplyOrder<TEntity>(this IQueryable<TEntity> query, GridifyQueryModel model)
4649
where TEntity : class
4750
{
48-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
51+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
52+
53+
model.OrderBy ??= mapper!.GetDefaultOrderExpression();
4954

5055
return query.AsNoTracking().ApplyOrdering(model, mapper);
5156
}
@@ -66,10 +71,25 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
6671
ColumnDistinctValueQueryModel model, Func<byte[], string>? decryptor = default,
6772
CancellationToken cancellationToken = default)
6873
{
69-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
74+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
7075

71-
if (!model.Encrypted)
76+
if (!mapper!.IsEncrypted(model.PropertyName))
7277
{
78+
if (mapper!.IsArray(model.PropertyName))
79+
{
80+
81+
var data = await query.ApplyFiltering(model, mapper)
82+
.ApplySelect(model.PropertyName, mapper)
83+
.ToArrayAsync(cancellationToken);
84+
85+
var result = data.SelectMany(item => ((IList)item).Cast<object>())
86+
.Distinct()
87+
.OrderBy(x => x)
88+
.ToList();
89+
90+
return new PagedResponse<object>(result, model.Page, model.PageSize, result.Count);
91+
}
92+
7393
return await query
7494
.ApplyFiltering(model, mapper)
7595
.ApplySelect(model.PropertyName, mapper)
@@ -92,40 +112,31 @@ public static async Task<PagedResponse<object>> ColumnDistinctValuesAsync<TEntit
92112
return new PagedResponse<object>([decryptedItem], 1, 1, 1);
93113
}
94114

95-
private static Expression<Func<T, object>> CreateSelector<T>(string propertyName)
96-
{
97-
var parameter = Expression.Parameter(typeof(T), "x");
98-
var property = Expression.Property(parameter, propertyName);
99-
var converted = Expression.Convert(property, typeof(object));
100-
101-
return Expression.Lambda<Func<T, object>>(converted, parameter);
102-
}
103-
104115
public static async Task<object> AggregateAsync<TEntity>(this IQueryable<TEntity> query,
105-
AggregateQueryModel model,
106-
CancellationToken cancellationToken = default)
107-
where TEntity : class
116+
AggregateQueryModel model,
117+
CancellationToken cancellationToken = default)
118+
where TEntity : class
108119
{
109120
var aggregateProperty = model.PropertyName;
110121

111-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
122+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
112123

113-
var query2 = query.ApplyFiltering(model, mapper).ApplySelect(aggregateProperty, mapper);
124+
var filteredQuery = query.ApplyFiltering(model, mapper).ApplySelect(aggregateProperty, mapper);
114125

115126
return model.AggregateType switch
116127
{
117-
AggregateType.UniqueCount => await query2.Distinct().CountAsync(cancellationToken),
118-
AggregateType.Sum => await query2.SumAsync(x => (decimal)x, cancellationToken),
119-
AggregateType.Average => await query2.AverageAsync(x => (decimal)x, cancellationToken),
120-
AggregateType.Min => await query2.MinAsync(cancellationToken),
121-
AggregateType.Max => await query2.MaxAsync(cancellationToken),
128+
AggregateType.UniqueCount => await filteredQuery.Distinct().CountAsync(cancellationToken),
129+
AggregateType.Sum => await filteredQuery.SumAsync(x => (decimal)x, cancellationToken),
130+
AggregateType.Average => await filteredQuery.AverageAsync(x => (decimal)x, cancellationToken),
131+
AggregateType.Min => await filteredQuery.MinAsync(cancellationToken),
132+
AggregateType.Max => await filteredQuery.MaxAsync(cancellationToken),
122133
_ => throw new NotImplementedException(),
123134
};
124135
}
125136

126137
public static IEnumerable<MappingModel> GetMappings<TEntity>()
127138
{
128-
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as GridifyMapper<TEntity>;
139+
var mapper = EntityGridifyMapperByType[typeof(TEntity)] as FilterMapper<TEntity>;
129140

130141
return mapper!.GetCurrentMaps().Select(x => new MappingModel
131142
{
@@ -138,4 +149,13 @@ public static IEnumerable<MappingModel> GetMappings<TEntity>()
138149
: x.To.Body.Type.Name,
139150
});
140151
}
152+
153+
private static Expression<Func<T, object>> CreateSelector<T>(string propertyName)
154+
{
155+
var parameter = Expression.Parameter(typeof(T), "x");
156+
var property = Expression.Property(parameter, propertyName);
157+
var converted = Expression.Convert(property, typeof(object));
158+
159+
return Expression.Lambda<Func<T, object>>(converted, parameter);
160+
}
141161
}

src/GridifyExtensions/Extensions/WebApplicationBuilderExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Gridify;
2+
using GridifyExtensions.Models;
23
using Microsoft.AspNetCore.Builder;
34
using System.Reflection;
45

@@ -22,7 +23,7 @@ public static WebApplicationBuilder AddGridify(this WebApplicationBuilder builde
2223
&& !t.IsAbstract
2324
&& t.BaseType != null
2425
&& t.BaseType.IsGenericType
25-
&& t.BaseType.GetGenericTypeDefinition() == typeof(GridifyMapper<>))
26+
&& t.BaseType.GetGenericTypeDefinition() == typeof(FilterMapper<>))
2627
.Select(x => new KeyValuePair<Type, object>(x.BaseType!.GetGenericArguments()[0], Activator.CreateInstance(x)!)))
2728
.ToDictionary(x => x.Key, x => x.Value);
2829

src/GridifyExtensions/GridifyExtensions.csproj

Lines changed: 2 additions & 2 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>1.1.0</Version>
11+
<Version>1.3.0</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>Stable release</PackageReleaseNotes>
17+
<PackageReleaseNotes>Ability to set default ordering using fluent API.</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>

src/GridifyExtensions/Models/ColumnDistinctValueQueryModel.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
public class ColumnDistinctValueQueryModel : GridifyQueryModel
44
{
55
public required string PropertyName { get; set; }
6-
public bool Encrypted { get; set; } = false;
76
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using Gridify;
2+
using GridifyExtensions.Abstractions;
3+
using System.Linq.Expressions;
4+
5+
namespace GridifyExtensions.Models;
6+
7+
8+
public class FilterMapper<T> : GridifyMapper<T>, IOrderThenBy
9+
{
10+
internal const string Desc = " desc";
11+
internal const string Separator = ", ";
12+
13+
private string _defaultOrderExpression = string.Empty;
14+
private readonly HashSet<string> _encryptedColumns = [];
15+
private readonly HashSet<string> _arrayColumns = [];
16+
17+
internal bool IsArray(string column) => _arrayColumns.Contains(column);
18+
internal bool IsEncrypted(string column) => _encryptedColumns.Contains(column);
19+
20+
internal string GetDefaultOrderExpression() => _defaultOrderExpression;
21+
22+
public IOrderThenBy AddDefaultOrderBy(string column)
23+
{
24+
_defaultOrderExpression = column;
25+
return this;
26+
}
27+
28+
public IOrderThenBy AddDefaultOrderByDescending(string column)
29+
{
30+
_defaultOrderExpression = column + Desc;
31+
return this;
32+
}
33+
34+
IOrderThenBy IOrderThenBy.ThenBy(string column)
35+
{
36+
_defaultOrderExpression += Separator + column;
37+
38+
return this;
39+
}
40+
41+
IOrderThenBy IOrderThenBy.ThenByDescending(string column)
42+
{
43+
_defaultOrderExpression += Separator + column + Desc;
44+
45+
return this;
46+
}
47+
48+
public IGridifyMapper<T> AddMap(string from,
49+
Expression<Func<T, object?>> to,
50+
Func<string, object>? convertor = null,
51+
bool overrideIfExists = true,
52+
bool isEncrypted = false,
53+
bool isArray = false)
54+
{
55+
if (isEncrypted)
56+
{
57+
_encryptedColumns.Add(from);
58+
}
59+
60+
if (isArray)
61+
{
62+
_arrayColumns.Add(from);
63+
}
64+
65+
return base.AddMap(from, to, convertor, overrideIfExists);
66+
}
67+
68+
public IGridifyMapper<T> AddMap(string from,
69+
Expression<Func<T, int, object?>> to,
70+
Func<string, object>? convertor = null,
71+
bool overrideIfExists = true,
72+
bool isEncrypted = false,
73+
bool isArray = false)
74+
{
75+
if (isEncrypted)
76+
{
77+
_encryptedColumns.Add(from);
78+
}
79+
80+
if (isArray)
81+
{
82+
_arrayColumns.Add(from);
83+
}
84+
85+
return base.AddMap(from, to, convertor, overrideIfExists);
86+
}
87+
}

0 commit comments

Comments
 (0)