diff --git a/Dapper.GraphQL/Contexts/SqlQueryContext.cs b/Dapper.GraphQL/Contexts/SqlQueryContext.cs index e35e7fe..2c2db3d 100644 --- a/Dapper.GraphQL/Contexts/SqlQueryContext.cs +++ b/Dapper.GraphQL/Contexts/SqlQueryContext.cs @@ -18,13 +18,13 @@ public SqlQueryContext(string alias = null, dynamic parameters = null) } } - public class SqlQueryContext + public class SqlQueryContext { protected List _splitOn; protected List _types; public DynamicParameters Parameters { get; set; } - protected Dapper.SqlBuilder SqlBuilder { get; set; } + protected DapperSqlBuilder SqlBuilder { get; set; } protected Dapper.SqlBuilder.Template QueryTemplate { get; set; } public SqlQueryContext(string from, dynamic parameters = null) @@ -32,13 +32,13 @@ public SqlQueryContext(string from, dynamic parameters = null) _splitOn = new List(); _types = new List(); Parameters = new DynamicParameters(parameters); - SqlBuilder = new Dapper.SqlBuilder(); + SqlBuilder = new DapperSqlBuilder(); // See https://github.com/StackExchange/Dapper/blob/master/Dapper.SqlBuilder/SqlBuilder.cs QueryTemplate = SqlBuilder.AddTemplate($@"SELECT /**select**/ FROM {from}/**innerjoin**//**leftjoin**//**rightjoin**//**join**/ -/**where**//**orderby**/"); +/**where**//**orderby**//**offset**//**top**/"); } /// @@ -178,7 +178,7 @@ public IEnumerable Execute( /// The options for the query (optional). /// A list of entities returned by the query. public async Task> ExecuteAsync( - IDbConnection connection, + IDbConnection connection, IHaveSelectionSet selectionSet, IEntityMapper mapper = null, IDbTransaction transaction = null, @@ -348,6 +348,77 @@ public SqlQueryContext OrderBy(string orderBy, dynamic parameters = null) return this; } + /// + /// Adds an Offset clause to allow pagination (it will skip N rows) + /// + /// + /// Order by clause is a must when using offset + /// + /// + /// var queryBuilder = new SqlQueryBuilder(); + /// queryBuilder.From("Customer customer"); + /// queryBuilder.Select( + /// "customer.id", + /// "customer.name", + /// ); + /// queryBuilder.SplitOn("id"); + /// queryBuilder.Where("customer.id == @id"); + /// queryBuilder.Parameters.Add("id", 1); + /// queryBuilder.Orderby("customer.name"); + /// queryBuilder.Offset(20); + /// var customer = queryBuilder + /// .Execute(dbConnection, graphQLSelectionSet); + /// .FirstOrDefault(); + /// + /// // SELECT customer.id, customer.name + /// // FROM Customer customer + /// // WHERE customer.id == @id + /// // ORDER BY customer.name + /// + /// total of rows to skip + /// The query builder + public SqlQueryContext Offset(int rowsToSkip) + { + SqlBuilder.Offset(rowsToSkip); + return this; + } + + /// + /// Adds a fetch clause to allow pagination + /// + /// + /// Order by clause is a must when using fetch + /// + /// + /// var queryBuilder = new SqlQueryBuilder(); + /// queryBuilder.From("Customer customer"); + /// queryBuilder.Select( + /// "customer.id", + /// "customer.name", + /// ); + /// queryBuilder.SplitOn("id"); + /// queryBuilder.Where("customer.id == @id"); + /// queryBuilder.Parameters.Add("id", 1); + /// queryBuilder.Orderby("customer.name"); + /// queryBuilder.Offset(20); + /// queryBuilder.Fetch(10); + /// var customer = queryBuilder + /// .Execute(dbConnection, graphQLSelectionSet); + /// .FirstOrDefault(); + /// + /// // SELECT customer.id, customer.name + /// // FROM Customer customer + /// // WHERE customer.id == @id + /// // ORDER BY customer.name + /// + /// total of rows to return + /// The query builder. + public SqlQueryContext Fetch(int rowsToReturn) + { + SqlBuilder.Fetch(rowsToReturn); + return this; + } + /// /// Adds a WHERE clause to the query, joining it with the previous with an 'OR' operator if needed. /// diff --git a/Dapper.GraphQL/DapperSqlBuilder.cs b/Dapper.GraphQL/DapperSqlBuilder.cs new file mode 100644 index 0000000..e0d8128 --- /dev/null +++ b/Dapper.GraphQL/DapperSqlBuilder.cs @@ -0,0 +1,86 @@ +namespace Dapper.GraphQL +{ + /// + /// A builder for SQL queries and statements inheriting the official Dapper.Sql Builder to extend its functions. + /// + public class DapperSqlBuilder : Dapper.SqlBuilder + { + /// + /// If the object has an offset there is no need to the fetch function to add an offset with 0 rows to skip + /// (offset clause is a must when using the fetch clause) + /// + private bool _hasOffset = false; + + /// + /// Adds an Offset clause to allow pagination (it will skip N rows) + /// + /// + /// Order by clause is a must when using offset + /// + /// + /// var queryBuilder = new SqlQueryBuilder(); + /// queryBuilder.From("Customer customer"); + /// queryBuilder.Select( + /// "customer.id", + /// "customer.name", + /// ); + /// queryBuilder.SplitOn("id"); + /// queryBuilder.Where("customer.id == @id"); + /// queryBuilder.Parameters.Add("id", 1); + /// queryBuilder.Orderby("customer.name"); + /// queryBuilder.Offset(20); + /// var customer = queryBuilder + /// .Execute(dbConnection, graphQLSelectionSet); + /// .FirstOrDefault(); + /// + /// // SELECT customer.id, customer.name + /// // FROM Customer customer + /// // WHERE customer.id == @id + /// // ORDER BY customer.name + /// + /// total of rows to skip + /// The query builder + public DapperSqlBuilder Offset(int rowsToSkip) + { + _hasOffset = true; + return AddClause("offset", $"{rowsToSkip}", null, " + ", "OFFSET ", " ROWS\n", false) as DapperSqlBuilder; + } + + /// + /// Adds a fetch clause to allow pagination + /// + /// + /// Order by clause is a must when using fetch + /// + /// + /// var queryBuilder = new SqlQueryBuilder(); + /// queryBuilder.From("Customer customer"); + /// queryBuilder.Select( + /// "customer.id", + /// "customer.name", + /// ); + /// queryBuilder.SplitOn("id"); + /// queryBuilder.Where("customer.id == @id"); + /// queryBuilder.Parameters.Add("id", 1); + /// queryBuilder.Orderby("customer.name"); + /// queryBuilder.Offset(20); + /// queryBuilder.Fetch(10); + /// var customer = queryBuilder + /// .Execute(dbConnection, graphQLSelectionSet); + /// .FirstOrDefault(); + /// + /// // SELECT customer.id, customer.name + /// // FROM Customer customer + /// // WHERE customer.id == @id + /// // ORDER BY customer.name + /// + /// total of rows to return + /// The query builder. + public DapperSqlBuilder Fetch(int rowsToReturn) + { + if(!_hasOffset) + Offset(0); + return AddClause("fetch", $"{rowsToReturn}", null, " + ", "FETCH FIRST ", " ROWS ONLY\n", false) as DapperSqlBuilder; + } + } +} \ No newline at end of file