Skip to content

Commit b76690c

Browse files
committed
Fix bug with Pagination not properly running Raw Sql that has a Where/Join clause in some use cases.
1 parent 14212c1 commit b76690c

File tree

7 files changed

+202
-168
lines changed

7 files changed

+202
-168
lines changed

RepoDbExtensions.PagingPrimitives/RepoDbExtensions.PagingPrimitives.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0;</TargetFrameworks>
55
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6-
<Version>1.13.1.2</Version>
7-
<AssemblyVersion>1.13.1.2</AssemblyVersion>
8-
<FileVersion>1.13.1.2</FileVersion>
6+
<Version>1.13.1.3</Version>
7+
<AssemblyVersion>1.13.1.3</AssemblyVersion>
8+
<FileVersion>1.13.1.3</FileVersion>
99
<Authors>BBernard / CajunCoding</Authors>
1010
<Company>CajunCoding</Company>
1111
<Description>The primitives and helpers needed for RepoDbExtensions.SqlServer.PagingOperations pacakge; used for working with modern pagination approaches such as Cursor based paging, as well as Offset based pagination, using the RepoDb ORM with Sql Server.</Description>

RepoDbExtensions.SqlServer.PagingOperations.Tests/PagingTestsUsingExecuteQueryApiForRawSql.cs

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public async Task TestCursorPagingWithRawSqlAsync()
2828
page = await sqlConnection.ExecutePagingCursorQueryAsync<CharacterDbModel>(
2929
//TEST Formatted SQL with line breaks, ending semi-colon, etc....
3030
commandText: @"
31-
SELECT *
32-
FROM [dbo].[StarWarsCharacters];
33-
",
31+
SELECT *
32+
FROM [dbo].[StarWarsCharacters] c;
33+
",
3434
new [] {OrderField.Descending<CharacterDbModel>(c => c.Id) },
3535
first: pageSize,
3636
afterCursor: page?.EndCursor,
@@ -73,61 +73,89 @@ public async Task TestCursorPagingWithRawSqlAsync()
7373
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
7474
}
7575

76+
[TestMethod]
77+
public async Task TestCursorPagingWithRawSqlWhereClauseAndNullPagingParamValuesAsync()
78+
{
79+
using var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false);
80+
81+
ICursorPageResults<CharacterDbModel> page = await sqlConnection.ExecutePagingCursorQueryAsync<CharacterDbModel>(
82+
//TEST Formatted SQL with line breaks, ending semi-colon, etc....
83+
commandText: @"
84+
SELECT *
85+
FROM [dbo].[StarWarsCharacters] c
86+
WHERE c.[Name] LIKE @NamePattern;
87+
",
88+
new[] { OrderField.Descending<CharacterDbModel>(c => c.Id) },
89+
//TEST PASSING IN Empty Paging Params (all values being NULL)....
90+
pagingParams: CursorPagingParams.ForCursors(null, null, null, null),
91+
sqlParams: new
92+
{
93+
NamePattern = "%Luke%"
94+
}
95+
);
96+
97+
page.Should().NotBeNull();
98+
page.TotalCount.Should().BeNull();
99+
page.CursorResults.Should().HaveCount(1);
100+
101+
TestContext.WriteLine("");
102+
TestContext.WriteLine($"[{page.PageCount}] Page Results:");
103+
}
104+
76105
[TestMethod]
77106
public async Task TestOffsetPagingWithRawSqlAsync()
78107
{
79-
using (var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false))
108+
using var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false);
109+
110+
const int pageSize = 2;
111+
int? totalCount = null;
112+
int runningTotal = 0;
113+
IOffsetPageResults<CharacterDbModel> page = null;
114+
115+
do
80116
{
81-
const int pageSize = 2;
82-
int? totalCount = null;
83-
int runningTotal = 0;
84-
IOffsetPageResults<CharacterDbModel> page = null;
117+
page = await sqlConnection.ExecutePagingOffsetQueryAsync<CharacterDbModel>(
118+
"SELECT * FROM [dbo].[StarWarsCharacters]",
119+
new[] { OrderField.Descending<CharacterDbModel>(c => c.Id) },
120+
skip: page?.EndIndex,
121+
take: pageSize,
122+
retrieveTotalCount: totalCount is null
123+
);
124+
125+
page.Should().NotBeNull();
126+
127+
var resultsList = page.Results.ToList();
128+
resultsList.Should().HaveCount(pageSize);
129+
130+
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
131+
if (totalCount is null)
132+
{
133+
page.TotalCount.Should().BePositive();
134+
totalCount = page.TotalCount;
135+
TestContext.WriteLine("*********************************************************");
136+
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
137+
TestContext.WriteLine("*********************************************************");
138+
}
139+
else
140+
{
141+
page.TotalCount.Should().BeNull();
142+
}
143+
144+
runningTotal += resultsList.Count;
85145

86-
do
146+
TestContext.WriteLine("");
147+
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
148+
TestContext.WriteLine("----------------------------------------");
149+
int counter = 0;
150+
foreach (var entity in resultsList)
87151
{
88-
page = await sqlConnection.ExecutePagingOffsetQueryAsync<CharacterDbModel>(
89-
"SELECT * FROM [dbo].[StarWarsCharacters]",
90-
new[] { OrderField.Descending<CharacterDbModel>(c => c.Id) },
91-
skip: page?.EndIndex,
92-
take: pageSize,
93-
retrieveTotalCount: totalCount is null
94-
);
95-
96-
page.Should().NotBeNull();
97-
98-
var resultsList = page.Results.ToList();
99-
resultsList.Should().HaveCount(pageSize);
100-
101-
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
102-
if (totalCount is null)
103-
{
104-
page.TotalCount.Should().BePositive();
105-
totalCount = page.TotalCount;
106-
TestContext.WriteLine("*********************************************************");
107-
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
108-
TestContext.WriteLine("*********************************************************");
109-
}
110-
else
111-
{
112-
page.TotalCount.Should().BeNull();
113-
}
114-
115-
runningTotal += resultsList.Count;
116-
117-
TestContext.WriteLine("");
118-
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
119-
TestContext.WriteLine("----------------------------------------");
120-
int counter = 0;
121-
foreach (var entity in resultsList)
122-
{
123-
TestContext.WriteLine($"[{++counter}] ==> ({entity.Id}) {entity.Name}");
124-
AssertCharacterDbModelIsValid(entity);
125-
}
126-
127-
} while (page.HasNextPage);
128-
129-
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
130-
}
152+
TestContext.WriteLine($"[{++counter}] ==> ({entity.Id}) {entity.Name}");
153+
AssertCharacterDbModelIsValid(entity);
154+
}
155+
156+
} while (page.HasNextPage);
157+
158+
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
131159
}
132160

133161
private void AssertCharacterDbModelIsValid(CharacterDbModel entity)

RepoDbExtensions.SqlServer.PagingOperations.Tests/PagingTestsUsingQueryApiForObjectExpressions.cs

Lines changed: 97 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -150,117 +150,115 @@ public async Task SimpleExampleOfOffsetPagingThroughDatasetUsingQueryApiWithWher
150150
[TestMethod]
151151
public async Task TestCursorPagingQueryApiSyntaxAsync()
152152
{
153-
using (var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false))
153+
using var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false);
154+
155+
const int pageSize = 2;
156+
int? totalCount = null;
157+
int runningTotal = 0;
158+
ICursorPageResults<CharacterDbModel> page = null;
159+
160+
do
154161
{
155-
const int pageSize = 2;
156-
int? totalCount = null;
157-
int runningTotal = 0;
158-
ICursorPageResults<CharacterDbModel> page = null;
162+
page = await sqlConnection.PagingCursorQueryAsync<CharacterDbModel>(
163+
orderBy: new [] {OrderField.Descending<CharacterDbModel>(c => c.Id) },
164+
pagingParams: CursorPagingParams.ForCursors(
165+
first: pageSize,
166+
afterCursor: page?.EndCursor,
167+
retrieveTotalCount: totalCount is null
168+
)
169+
);
170+
171+
page.Should().NotBeNull();
172+
173+
var resultsList = page.CursorResults.ToList();
174+
resultsList.Should().HaveCount(pageSize);
159175

160-
do
176+
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
177+
if (totalCount is null)
161178
{
162-
page = await sqlConnection.PagingCursorQueryAsync<CharacterDbModel>(
163-
orderBy: new [] {OrderField.Descending<CharacterDbModel>(c => c.Id) },
164-
pagingParams: CursorPagingParams.ForCursors(
165-
first: pageSize,
166-
afterCursor: page?.EndCursor,
167-
retrieveTotalCount: totalCount is null
168-
)
169-
);
170-
171-
page.Should().NotBeNull();
172-
173-
var resultsList = page.CursorResults.ToList();
174-
resultsList.Should().HaveCount(pageSize);
175-
176-
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
177-
if (totalCount is null)
178-
{
179-
page.TotalCount.Should().BePositive();
180-
totalCount = page.TotalCount;
181-
TestContext.WriteLine("*********************************************************");
182-
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
183-
TestContext.WriteLine("*********************************************************");
184-
}
185-
else
186-
{
187-
page.TotalCount.Should().BeNull();
188-
}
179+
page.TotalCount.Should().BePositive();
180+
totalCount = page.TotalCount;
181+
TestContext.WriteLine("*********************************************************");
182+
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
183+
TestContext.WriteLine("*********************************************************");
184+
}
185+
else
186+
{
187+
page.TotalCount.Should().BeNull();
188+
}
189189

190-
runningTotal += resultsList.Count;
191-
192-
TestContext.WriteLine("");
193-
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
194-
TestContext.WriteLine("----------------------------------------");
195-
foreach (var result in resultsList)
196-
{
197-
var entity = result.Entity;
198-
TestContext.WriteLine($"[{result.Cursor}] ==> ({entity.Id}) {entity.Name}");
199-
AssertCharacterDbModelIsValid(entity);
200-
}
201-
202-
} while (page.HasNextPage);
203-
204-
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
205-
}
190+
runningTotal += resultsList.Count;
191+
192+
TestContext.WriteLine("");
193+
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
194+
TestContext.WriteLine("----------------------------------------");
195+
foreach (var result in resultsList)
196+
{
197+
var entity = result.Entity;
198+
TestContext.WriteLine($"[{result.Cursor}] ==> ({entity.Id}) {entity.Name}");
199+
AssertCharacterDbModelIsValid(entity);
200+
}
201+
202+
} while (page.HasNextPage);
203+
204+
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
206205
}
207206

208207
[TestMethod]
209208
public async Task TestOffsetPagingQueryApiSyntaxAsync()
210209
{
211-
using (var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false))
210+
using var sqlConnection = await CreateSqlConnectionAsync().ConfigureAwait(false);
211+
212+
const int pageSize = 2;
213+
int? totalCount = null;
214+
int runningTotal = 0;
215+
IOffsetPageResults<CharacterDbModel> page = null;
216+
217+
do
212218
{
213-
const int pageSize = 2;
214-
int? totalCount = null;
215-
int runningTotal = 0;
216-
IOffsetPageResults<CharacterDbModel> page = null;
219+
page = await sqlConnection.PagingOffsetQueryAsync<CharacterDbModel>(
220+
orderBy: new[] { OrderField.Descending<CharacterDbModel>(c => c.Id) },
221+
pagingParams: OffsetPagingParams.ForSkipTake(
222+
take: pageSize,
223+
skip: page?.EndIndex,
224+
retrieveTotalCount: totalCount is null
225+
)
226+
);
217227

218-
do
228+
page.Should().NotBeNull();
229+
230+
var resultsList = page.Results.ToList();
231+
resultsList.Should().HaveCount(pageSize);
232+
233+
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
234+
if (totalCount is null)
219235
{
220-
page = await sqlConnection.PagingOffsetQueryAsync<CharacterDbModel>(
221-
orderBy: new[] { OrderField.Descending<CharacterDbModel>(c => c.Id) },
222-
pagingParams: OffsetPagingParams.ForSkipTake(
223-
take: pageSize,
224-
skip: page?.EndIndex,
225-
retrieveTotalCount: totalCount is null
226-
)
227-
);
228-
229-
page.Should().NotBeNull();
230-
231-
var resultsList = page.Results.ToList();
232-
resultsList.Should().HaveCount(pageSize);
233-
234-
//Validate that we get Total Count only once, and on all following pages it is skipped and Null is returned as expected!
235-
if (totalCount is null)
236-
{
237-
page.TotalCount.Should().BePositive();
238-
totalCount = page.TotalCount;
239-
TestContext.WriteLine("*********************************************************");
240-
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
241-
TestContext.WriteLine("*********************************************************");
242-
}
243-
else
244-
{
245-
page.TotalCount.Should().BeNull();
246-
}
247-
248-
runningTotal += resultsList.Count;
249-
250-
TestContext.WriteLine("");
251-
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
252-
TestContext.WriteLine("----------------------------------------");
253-
int counter = 0;
254-
foreach (var entity in resultsList)
255-
{
256-
TestContext.WriteLine($"[{++counter}] ==> ({entity.Id}) {entity.Name}");
257-
AssertCharacterDbModelIsValid(entity);
258-
}
259-
260-
} while (page.HasNextPage);
261-
262-
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
263-
}
236+
page.TotalCount.Should().BePositive();
237+
totalCount = page.TotalCount;
238+
TestContext.WriteLine("*********************************************************");
239+
TestContext.WriteLine($"[{totalCount}] Total Results to be processed...");
240+
TestContext.WriteLine("*********************************************************");
241+
}
242+
else
243+
{
244+
page.TotalCount.Should().BeNull();
245+
}
246+
247+
runningTotal += resultsList.Count;
248+
249+
TestContext.WriteLine("");
250+
TestContext.WriteLine($"[{resultsList.Count}] Page Results:");
251+
TestContext.WriteLine("----------------------------------------");
252+
int counter = 0;
253+
foreach (var entity in resultsList)
254+
{
255+
TestContext.WriteLine($"[{++counter}] ==> ({entity.Id}) {entity.Name}");
256+
AssertCharacterDbModelIsValid(entity);
257+
}
258+
259+
} while (page.HasNextPage);
260+
261+
Assert.AreEqual(totalCount, runningTotal, "Total Count doesn't Match the final running total tally!");
264262
}
265263

266264
private void AssertCharacterDbModelIsValid(CharacterDbModel entity)
@@ -282,8 +280,6 @@ private void AssertCharacterDbModelIsValid(CharacterDbModel entity)
282280
? "Protocol"
283281
: "Astromech"
284282
);
285-
286-
287283
}
288284
}
289285
}

0 commit comments

Comments
 (0)