Skip to content

Commit 339e080

Browse files
authored
Merge c3428cc into ce0b247
2 parents ce0b247 + c3428cc commit 339e080

7 files changed

+233
-5
lines changed

Paginator.EntityFrameworkCore.csproj

+8-2
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@
1313
<RepositoryUrl>https://github.com/tmacharia/paginator.efcore</RepositoryUrl>
1414
<PackageTags>linq, pagination, paginator, paging, efcore</PackageTags>
1515
<PackageId>Paginator.EntityFrameworkCore</PackageId>
16-
<Version>3.1.5-rc</Version>
16+
<Version>3.1.15</Version>
1717
<NeutralLanguage>en</NeutralLanguage>
1818
<PackageLicenseUrl></PackageLicenseUrl>
1919
<RepositoryType>git</RepositoryType>
2020
<PackageLicenseFile>LICENSE</PackageLicenseFile>
2121
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2222
<Description>
23-
Asynchronous pagination of queries to your data store using EntityFrameworkCore.
23+
Asynchronous &amp; Synchronous pagination of queries to your data store using EntityFrameworkCore. With the help of some simple to use extension methods, querying portions/chunks of data just gets easier.
2424
</Description>
2525
</PropertyGroup>
26+
27+
<ItemGroup>
28+
<Compile Remove="tests\**" />
29+
<EmbeddedResource Remove="tests\**" />
30+
<None Remove="tests\**" />
31+
</ItemGroup>
2632

2733
<ItemGroup>
2834
<None Include=".\LICENSE">

Paginator.EntityFrameworkCore.sln

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 16
44
VisualStudioVersion = 16.0.31004.235
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Paginator.EntityFrameworkCore", "Paginator.EntityFrameworkCore.csproj", "{2642CB54-145C-4403-92ED-13B000A78E4E}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Paginator.EntityFrameworkCore", "Paginator.EntityFrameworkCore.csproj", "{2642CB54-145C-4403-92ED-13B000A78E4E}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{96E4E2AA-FD17-498F-A62C-3F6ACA3B00DF}"
79
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
1517
{2642CB54-145C-4403-92ED-13B000A78E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
1618
{2642CB54-145C-4403-92ED-13B000A78E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
1719
{2642CB54-145C-4403-92ED-13B000A78E4E}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{96E4E2AA-FD17-498F-A62C-3F6ACA3B00DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{96E4E2AA-FD17-498F-A62C-3F6ACA3B00DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{96E4E2AA-FD17-498F-A62C-3F6ACA3B00DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{96E4E2AA-FD17-498F-A62C-3F6ACA3B00DF}.Release|Any CPU.Build.0 = Release|Any CPU
1824
EndGlobalSection
1925
GlobalSection(SolutionProperties) = preSolution
2026
HideSolutionNode = FALSE

Paginator.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public static class Paginator
2626
/// <param name="skipCount">Specify whether to omit running a count operation on your query againt the data store.</param>
2727
/// <param name="token">Cancellation token.</param>
2828
/// <returns>A task that represents the asynchronous operation which you can await.</returns>
29+
/// <exception cref="ArgumentException"/>
30+
/// <exception cref="ArgumentNullException"/>
2931
/// <exception cref="OperationCanceledException"/>
3032
public static Task<PagedResult<TEntity>> PaginateAsync<TEntity>(this IQueryable<TEntity> query,
3133
int page = DEF_PAGE, int perpage = DEF_PERPAGE, bool skipCount = DEF_SKIPCOUNT, CancellationToken token = default)
@@ -40,7 +42,8 @@ public static Task<PagedResult<TEntity>> PaginateAsync<TEntity>(this IQueryable<
4042
/// <param name="perpage">Items per page.</param>
4143
/// <param name="skipCount">Specify whether to omit running a count operation on your query againt the data store.</param>
4244
/// <returns>A <see cref="PagedResult{TEntity}"/> response object.</returns>
43-
/// <exception cref="OperationCanceledException"/>
45+
/// <exception cref="ArgumentException"/>
46+
/// <exception cref="ArgumentNullException"/>
4447
public static PagedResult<TEntity> Paginate<TEntity>(this IQueryable<TEntity> query,
4548
int page = DEF_PAGE, int perpage = DEF_PERPAGE, bool skipCount = DEF_SKIPCOUNT)
4649
where TEntity : class
@@ -49,6 +52,8 @@ public static PagedResult<TEntity> Paginate<TEntity>(this IQueryable<TEntity> qu
4952
internal static async Task<PagedResult<TEntity>> ProcessPaginationAsync<TEntity>(this IQueryable<TEntity> query,
5053
int page = DEF_PAGE, int perpage = DEF_PERPAGE, bool skipCount = DEF_SKIPCOUNT, CancellationToken token = default)
5154
{
55+
ValidateParams_IfInvalid_Throw(page, perpage);
56+
5257
int total = 0;
5358
var list = new List<TEntity>();
5459

@@ -75,6 +80,8 @@ internal static async Task<PagedResult<TEntity>> ProcessPaginationAsync<TEntity>
7580
internal static PagedResult<TEntity> ProcessPagination<TEntity>(this IQueryable<TEntity> query,
7681
int page = DEF_PAGE, int perpage = DEF_PERPAGE, bool skipCount = DEF_SKIPCOUNT)
7782
{
83+
ValidateParams_IfInvalid_Throw(page, perpage);
84+
7885
int total = 0;
7986
var list = new List<TEntity>();
8087

@@ -96,6 +103,17 @@ internal static PagedResult<TEntity> ProcessPagination<TEntity>(this IQueryable<
96103
Items = list
97104
};
98105
}
106+
107+
internal static void ValidateParams_IfInvalid_Throw(int page, int perpage)
108+
{
109+
if (page <= 0)
110+
throw new ArgumentException("Page parameter must be greater than zero.", nameof(page));
111+
112+
if (perpage < 0)
113+
throw new ArgumentException("Per-page parameter must be 0 or greater than 0.", nameof(perpage));
114+
115+
return;
116+
}
99117
internal static Task<int> CountEntitiesAsync<TEntity>(this IQueryable<TEntity> query, CancellationToken token = default)
100118
{
101119
return query.CountAsync(token);
@@ -107,7 +125,6 @@ internal static int CalculateTotalPages(int totalItems, int perpage)
107125
{
108126
ans = totalItems / perpage;
109127
ans += (totalItems % perpage) > 0 ? 1 : 0;
110-
return ans;
111128
}
112129
return ans;
113130
}

tests/TestBaseContext.cs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace tests
4+
{
5+
internal class TestBaseContext
6+
{
7+
private TestDbContext _context;
8+
private DbContextOptions<TestDbContext> DbContextOptions;
9+
10+
public TestBaseContext()
11+
{ }
12+
13+
protected TestDbContext Context
14+
{
15+
get
16+
{
17+
if (_context == null)
18+
{
19+
DbContextOptions = new DbContextOptionsBuilder<TestDbContext>()
20+
.UseInMemoryDatabase(databaseName: "test_db")
21+
.EnableServiceProviderCaching(true)
22+
.EnableSensitiveDataLogging(true)
23+
.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)
24+
.Options;
25+
_context = new TestDbContext(DbContextOptions);
26+
}
27+
return _context;
28+
}
29+
}
30+
31+
32+
internal T Add<T>(T obj, bool save = true)
33+
where T : class
34+
{
35+
obj = Context.Add(obj).Entity;
36+
if (save)
37+
Save();
38+
return obj;
39+
}
40+
protected void Save() => Context.SaveChanges();
41+
}
42+
}

tests/TestDbContext.cs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace tests
4+
{
5+
internal class TestDbContext : DbContext
6+
{
7+
public TestDbContext(DbContextOptions<TestDbContext> options)
8+
:base(options)
9+
{ }
10+
11+
public DbSet<Car> Cars { get; set; }
12+
}
13+
internal class Car
14+
{
15+
public int Id { get; set; }
16+
}
17+
}

tests/UnitTests.cs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System.Linq;
2+
using Paginator.EntityFrameworkCore;
3+
using NUnit.Framework;
4+
using System;
5+
using System.Threading.Tasks;
6+
using System.Threading;
7+
8+
namespace tests
9+
{
10+
internal class UnitTests : TestBaseContext
11+
{
12+
[SetUp]
13+
public void Setup()
14+
{
15+
}
16+
17+
#region Synchronous
18+
[Test]
19+
[Order(0)]
20+
public void Pg_Empty()
21+
{
22+
var list = Context.Cars.Paginate(1, 10);
23+
Assert.IsNotNull(list);
24+
Assert.Zero(list.TotalItems);
25+
}
26+
[Test]
27+
[Order(1)]
28+
public void Pg_With_Items()
29+
{
30+
Add(new Car());
31+
Add(new Car());
32+
Add(new Car());
33+
Add(new Car());
34+
35+
var list = Context.Cars.Paginate(1, 2);
36+
Assert.IsNotNull(list);
37+
Assert.AreEqual(4, list.TotalItems);
38+
Assert.AreEqual(2, list.TotalPages);
39+
}
40+
[Test]
41+
[Order(2)]
42+
public void Pg_Skip_Count()
43+
{
44+
Add(new Car());
45+
Add(new Car());
46+
Add(new Car());
47+
Add(new Car());
48+
49+
var list = Context.Cars.Paginate(1, 2, true);
50+
Assert.IsNotNull(list);
51+
Assert.AreEqual(2, list.TotalItems);
52+
Assert.AreEqual(1, list.TotalPages);
53+
}
54+
[Test]
55+
[Order(2)]
56+
public void Pg_Invalid_PageParam_ThrowEx()
57+
{
58+
Assert.Throws<ArgumentException>(() => Context.Cars.Paginate(0, 2));
59+
Assert.Throws<ArgumentException>(() => Context.Cars.Paginate(-1, 2));
60+
}
61+
[Test]
62+
[Order(2)]
63+
public void Pg_Invalid_PerPageParam_ThrowEx()
64+
{
65+
Assert.Throws<ArgumentException>(() => Context.Cars.Paginate(1, -1));
66+
}
67+
#endregion
68+
69+
#region Asynchronous
70+
[Test]
71+
[Order(0)]
72+
public async Task AsyncPg_Empty()
73+
{
74+
var list = await Context.Cars.PaginateAsync(1, 10);
75+
Assert.IsNotNull(list);
76+
Assert.Zero(list.TotalItems);
77+
}
78+
[Test]
79+
[Order(1)]
80+
public async Task AsyncPg_With_Items()
81+
{
82+
var list = await Context.Cars.PaginateAsync(1, 2);
83+
Assert.IsNotNull(list);
84+
Assert.AreEqual(4, list.TotalItems);
85+
Assert.AreEqual(2, list.TotalPages);
86+
}
87+
[Test]
88+
[Order(2)]
89+
public async Task AsyncPg_Skip_Count()
90+
{
91+
var list = await Context.Cars.PaginateAsync(1, 2, true);
92+
Assert.IsNotNull(list);
93+
Assert.AreEqual(2, list.TotalItems);
94+
Assert.AreEqual(1, list.TotalPages);
95+
}
96+
[Test]
97+
[Order(2)]
98+
public void AsyncPg_Invalid_PageParam_ThrowEx()
99+
{
100+
Assert.ThrowsAsync<ArgumentException>(() => Context.Cars.PaginateAsync(0, 2));
101+
Assert.ThrowsAsync<ArgumentException>(() => Context.Cars.PaginateAsync(-1, 2));
102+
}
103+
[Test]
104+
[Order(2)]
105+
public void AsyncPg_Invalid_PerPageParam_ThrowEx()
106+
{
107+
Assert.ThrowsAsync<ArgumentException>(() => Context.Cars.PaginateAsync(1, -1));
108+
}
109+
[Test]
110+
[Order(3)]
111+
public void AsyncPg_CancelledToken_Throw()
112+
{
113+
var src = new CancellationTokenSource();
114+
src.Cancel();
115+
Assert.ThrowsAsync<OperationCanceledException>(() => Context.Cars.PaginateAsync(1, 2, token: src.Token));
116+
}
117+
#endregion
118+
}
119+
}

tests/tests.csproj

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Moq" Version="4.16.1" />
11+
<PackageReference Include="NUnit" Version="3.13.2" />
12+
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.15" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\Paginator.EntityFrameworkCore.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)