From ca5937025b7a7b566bbcc0e72ba80a94dc52abfe Mon Sep 17 00:00:00 2001 From: "Barbosa, William" Date: Fri, 12 Jul 2019 15:25:46 -0300 Subject: [PATCH 1/2] Added offset and fetch functions to the SqlBuilder to allow pagination --- Dapper.GraphQL/Contexts/SqlQueryContext.cs | 81 ++++++++++++++++++-- Dapper.GraphQL/DapperSqlBuilder.cs | 86 ++++++++++++++++++++++ Dapper.GraphQL/SqlBuilder.cs | 5 ++ 3 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 Dapper.GraphQL/DapperSqlBuilder.cs 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 diff --git a/Dapper.GraphQL/SqlBuilder.cs b/Dapper.GraphQL/SqlBuilder.cs index c5dceb5..ac4240e 100644 --- a/Dapper.GraphQL/SqlBuilder.cs +++ b/Dapper.GraphQL/SqlBuilder.cs @@ -29,6 +29,11 @@ public static SqlQueryContext From(string from, dynamic parameters = null) return new SqlQueryContext(from, parameters); } + public static SqlQueryContext RawSql(string sql, dynamic parameteres) + { + return new SqlQueryContext(sql, parameteres); + } + public static SqlQueryContext From(string alias = null) where TEntityType : class { From ed28b99a7e82d6fd93f17e0f8fb561f69a454b05 Mon Sep 17 00:00:00 2001 From: "Barbosa, William" Date: Fri, 12 Jul 2019 15:31:27 -0300 Subject: [PATCH 2/2] Removing RawSql Function because it is not needed --- Dapper.GraphQL/SqlBuilder.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Dapper.GraphQL/SqlBuilder.cs b/Dapper.GraphQL/SqlBuilder.cs index ac4240e..c5dceb5 100644 --- a/Dapper.GraphQL/SqlBuilder.cs +++ b/Dapper.GraphQL/SqlBuilder.cs @@ -29,11 +29,6 @@ public static SqlQueryContext From(string from, dynamic parameters = null) return new SqlQueryContext(from, parameters); } - public static SqlQueryContext RawSql(string sql, dynamic parameteres) - { - return new SqlQueryContext(sql, parameteres); - } - public static SqlQueryContext From(string alias = null) where TEntityType : class {