Skip to content

Commit d48fc1f

Browse files
author
MPCoreDeveloper
committed
extensions update full dapper support enhanced healthchecks
1 parent 1147dc4 commit d48fc1f

15 files changed

+4302
-25
lines changed
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
using Dapper;
2+
using SharpCoreDB.Interfaces;
3+
using System.Data;
4+
5+
namespace SharpCoreDB.Extensions;
6+
7+
/// <summary>
8+
/// Provides async extension methods for Dapper with SharpCoreDB.
9+
/// </summary>
10+
public static class DapperAsyncExtensions
11+
{
12+
/// <summary>
13+
/// Executes a query asynchronously and returns the results as dynamic objects.
14+
/// </summary>
15+
/// <param name="database">The database instance.</param>
16+
/// <param name="sql">The SQL query.</param>
17+
/// <param name="param">The query parameters.</param>
18+
/// <param name="cancellationToken">Cancellation token.</param>
19+
/// <returns>The query results as dynamic objects.</returns>
20+
public static async Task<IEnumerable<dynamic>> QueryAsync(
21+
this IDatabase database,
22+
string sql,
23+
object? param = null,
24+
CancellationToken cancellationToken = default)
25+
{
26+
ArgumentNullException.ThrowIfNull(database);
27+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
28+
29+
using var connection = database.GetDapperConnection();
30+
connection.Open();
31+
32+
var parameters = param != null
33+
? ConvertToDynamicParameters(param)
34+
: null;
35+
36+
return await connection.QueryAsync(sql, parameters);
37+
}
38+
39+
/// <summary>
40+
/// Executes a query asynchronously and returns the results as strongly-typed objects.
41+
/// </summary>
42+
/// <typeparam name="T">The result type.</typeparam>
43+
/// <param name="database">The database instance.</param>
44+
/// <param name="sql">The SQL query.</param>
45+
/// <param name="param">The query parameters.</param>
46+
/// <param name="cancellationToken">Cancellation token.</param>
47+
/// <returns>The query results.</returns>
48+
public static async Task<IEnumerable<T>> QueryAsync<T>(
49+
this IDatabase database,
50+
string sql,
51+
object? param = null,
52+
CancellationToken cancellationToken = default)
53+
{
54+
ArgumentNullException.ThrowIfNull(database);
55+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
56+
57+
using var connection = database.GetDapperConnection();
58+
connection.Open();
59+
60+
var parameters = param != null
61+
? ConvertToDynamicParameters(param)
62+
: null;
63+
64+
return await connection.QueryAsync<T>(sql, parameters);
65+
}
66+
67+
/// <summary>
68+
/// Executes a query asynchronously and returns a single result.
69+
/// </summary>
70+
/// <typeparam name="T">The result type.</typeparam>
71+
/// <param name="database">The database instance.</param>
72+
/// <param name="sql">The SQL query.</param>
73+
/// <param name="param">The query parameters.</param>
74+
/// <param name="cancellationToken">Cancellation token.</param>
75+
/// <returns>The first result or default.</returns>
76+
public static async Task<T?> QueryFirstOrDefaultAsync<T>(
77+
this IDatabase database,
78+
string sql,
79+
object? param = null,
80+
CancellationToken cancellationToken = default)
81+
{
82+
ArgumentNullException.ThrowIfNull(database);
83+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
84+
85+
using var connection = database.GetDapperConnection();
86+
connection.Open();
87+
88+
var parameters = param != null
89+
? ConvertToDynamicParameters(param)
90+
: null;
91+
92+
return await connection.QueryFirstOrDefaultAsync<T>(sql, parameters);
93+
}
94+
95+
/// <summary>
96+
/// Executes a query asynchronously and returns a single result.
97+
/// </summary>
98+
/// <typeparam name="T">The result type.</typeparam>
99+
/// <param name="database">The database instance.</param>
100+
/// <param name="sql">The SQL query.</param>
101+
/// <param name="param">The query parameters.</param>
102+
/// <param name="cancellationToken">Cancellation token.</param>
103+
/// <returns>The single result.</returns>
104+
/// <exception cref="InvalidOperationException">Thrown when no results or multiple results are found.</exception>
105+
public static async Task<T> QuerySingleAsync<T>(
106+
this IDatabase database,
107+
string sql,
108+
object? param = null,
109+
CancellationToken cancellationToken = default)
110+
{
111+
ArgumentNullException.ThrowIfNull(database);
112+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
113+
114+
using var connection = database.GetDapperConnection();
115+
connection.Open();
116+
117+
var parameters = param != null
118+
? ConvertToDynamicParameters(param)
119+
: null;
120+
121+
return await connection.QuerySingleAsync<T>(sql, parameters);
122+
}
123+
124+
/// <summary>
125+
/// Executes a command asynchronously and returns the number of affected rows.
126+
/// </summary>
127+
/// <param name="database">The database instance.</param>
128+
/// <param name="sql">The SQL command.</param>
129+
/// <param name="param">The command parameters.</param>
130+
/// <param name="cancellationToken">Cancellation token.</param>
131+
/// <returns>The number of rows affected.</returns>
132+
public static async Task<int> ExecuteAsync(
133+
this IDatabase database,
134+
string sql,
135+
object? param = null,
136+
CancellationToken cancellationToken = default)
137+
{
138+
ArgumentNullException.ThrowIfNull(database);
139+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
140+
141+
using var connection = database.GetDapperConnection();
142+
connection.Open();
143+
144+
var parameters = param != null
145+
? ConvertToDynamicParameters(param)
146+
: null;
147+
148+
return await connection.ExecuteAsync(sql, parameters);
149+
}
150+
151+
/// <summary>
152+
/// Executes a scalar query asynchronously.
153+
/// </summary>
154+
/// <typeparam name="T">The scalar result type.</typeparam>
155+
/// <param name="database">The database instance.</param>
156+
/// <param name="sql">The SQL query.</param>
157+
/// <param name="param">The query parameters.</param>
158+
/// <param name="cancellationToken">Cancellation token.</param>
159+
/// <returns>The scalar result.</returns>
160+
public static async Task<T?> ExecuteScalarAsync<T>(
161+
this IDatabase database,
162+
string sql,
163+
object? param = null,
164+
CancellationToken cancellationToken = default)
165+
{
166+
ArgumentNullException.ThrowIfNull(database);
167+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
168+
169+
using var connection = database.GetDapperConnection();
170+
connection.Open();
171+
172+
var parameters = param != null
173+
? ConvertToDynamicParameters(param)
174+
: null;
175+
176+
return await connection.ExecuteScalarAsync<T>(sql, parameters);
177+
}
178+
179+
/// <summary>
180+
/// Executes multiple queries asynchronously.
181+
/// </summary>
182+
/// <param name="database">The database instance.</param>
183+
/// <param name="sql">The SQL containing multiple queries.</param>
184+
/// <param name="param">The query parameters.</param>
185+
/// <param name="cancellationToken">Cancellation token.</param>
186+
/// <returns>A multi-result reader.</returns>
187+
public static async Task<SqlMapper.GridReader> QueryMultipleAsync(
188+
this IDatabase database,
189+
string sql,
190+
object? param = null,
191+
CancellationToken cancellationToken = default)
192+
{
193+
ArgumentNullException.ThrowIfNull(database);
194+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
195+
196+
var connection = database.GetDapperConnection();
197+
connection.Open();
198+
199+
var parameters = param != null
200+
? ConvertToDynamicParameters(param)
201+
: null;
202+
203+
return await connection.QueryMultipleAsync(sql, parameters);
204+
}
205+
206+
/// <summary>
207+
/// Executes a query with pagination asynchronously.
208+
/// </summary>
209+
/// <typeparam name="T">The result type.</typeparam>
210+
/// <param name="database">The database instance.</param>
211+
/// <param name="sql">The SQL query.</param>
212+
/// <param name="pageNumber">The page number (1-based).</param>
213+
/// <param name="pageSize">The page size.</param>
214+
/// <param name="param">The query parameters.</param>
215+
/// <param name="cancellationToken">Cancellation token.</param>
216+
/// <returns>A paginated result.</returns>
217+
public static async Task<PagedResult<T>> QueryPagedAsync<T>(
218+
this IDatabase database,
219+
string sql,
220+
int pageNumber,
221+
int pageSize,
222+
object? param = null,
223+
CancellationToken cancellationToken = default)
224+
{
225+
ArgumentNullException.ThrowIfNull(database);
226+
ArgumentException.ThrowIfNullOrWhiteSpace(sql);
227+
228+
if (pageNumber < 1)
229+
throw new ArgumentException("Page number must be at least 1", nameof(pageNumber));
230+
231+
if (pageSize < 1)
232+
throw new ArgumentException("Page size must be at least 1", nameof(pageSize));
233+
234+
using var connection = database.GetDapperConnection();
235+
connection.Open();
236+
237+
var parameters = param != null
238+
? ConvertToDynamicParameters(param)
239+
: null;
240+
241+
// Get total count
242+
var countSql = $"SELECT COUNT(*) FROM ({sql}) AS CountQuery";
243+
var totalCount = await connection.ExecuteScalarAsync<long>(countSql, parameters);
244+
245+
// Get paged data
246+
var offset = (pageNumber - 1) * pageSize;
247+
var pagedSql = $"{sql} LIMIT {pageSize} OFFSET {offset}";
248+
var items = await connection.QueryAsync<T>(pagedSql, parameters);
249+
250+
return new PagedResult<T>
251+
{
252+
Items = items.ToList(),
253+
PageNumber = pageNumber,
254+
PageSize = pageSize,
255+
TotalCount = totalCount
256+
};
257+
}
258+
259+
/// <summary>
260+
/// Executes a stored procedure asynchronously (if supported in future).
261+
/// </summary>
262+
/// <typeparam name="T">The result type.</typeparam>
263+
/// <param name="database">The database instance.</param>
264+
/// <param name="procedureName">The stored procedure name.</param>
265+
/// <param name="param">The procedure parameters.</param>
266+
/// <param name="cancellationToken">Cancellation token.</param>
267+
/// <returns>The procedure results.</returns>
268+
public static async Task<IEnumerable<T>> ExecuteStoredProcedureAsync<T>(
269+
this IDatabase database,
270+
string procedureName,
271+
object? param = null,
272+
CancellationToken cancellationToken = default)
273+
{
274+
ArgumentNullException.ThrowIfNull(database);
275+
ArgumentException.ThrowIfNullOrWhiteSpace(procedureName);
276+
277+
using var connection = database.GetDapperConnection();
278+
connection.Open();
279+
280+
var parameters = param != null
281+
? ConvertToDynamicParameters(param)
282+
: null;
283+
284+
return await connection.QueryAsync<T>(
285+
procedureName,
286+
parameters,
287+
commandType: CommandType.StoredProcedure);
288+
}
289+
290+
private static DynamicParameters ConvertToDynamicParameters(object param)
291+
{
292+
if (param is DynamicParameters dp)
293+
return dp;
294+
295+
var dynamicParams = new DynamicParameters();
296+
297+
var properties = param.GetType().GetProperties();
298+
foreach (var prop in properties)
299+
{
300+
var value = prop.GetValue(param);
301+
dynamicParams.Add(prop.Name, value);
302+
}
303+
304+
return dynamicParams;
305+
}
306+
}
307+
308+
/// <summary>
309+
/// Represents a paginated query result.
310+
/// </summary>
311+
/// <typeparam name="T">The item type.</typeparam>
312+
public class PagedResult<T>
313+
{
314+
/// <summary>
315+
/// Gets or sets the items in the current page.
316+
/// </summary>
317+
public List<T> Items { get; set; } = [];
318+
319+
/// <summary>
320+
/// Gets or sets the current page number (1-based).
321+
/// </summary>
322+
public int PageNumber { get; set; }
323+
324+
/// <summary>
325+
/// Gets or sets the page size.
326+
/// </summary>
327+
public int PageSize { get; set; }
328+
329+
/// <summary>
330+
/// Gets or sets the total count of items.
331+
/// </summary>
332+
public long TotalCount { get; set; }
333+
334+
/// <summary>
335+
/// Gets the total number of pages.
336+
/// </summary>
337+
public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize);
338+
339+
/// <summary>
340+
/// Gets whether there is a previous page.
341+
/// </summary>
342+
public bool HasPreviousPage => PageNumber > 1;
343+
344+
/// <summary>
345+
/// Gets whether there is a next page.
346+
/// </summary>
347+
public bool HasNextPage => PageNumber < TotalPages;
348+
}

0 commit comments

Comments
 (0)