Utility class for building SQL queries with parameters based on C# string interpolation.
$ dotnet add package GennadyGS.SqlQueryBuilderA powerful, type-safe SQL query builder for .NET that leverages C# string interpolation to create parameterized SQL queries with automatic SQL injection protection.
Install via NuGet Package Manager:
Install-Package GennadyGS.SqlQueryBuilder
Or via .NET CLI:
dotnet add package GennadyGS.SqlQueryBuilder
using SqlQueryBuilders;
// Simple parameterized query
SqlQueryBuilder query = $"SELECT * FROM Users WHERE Id = {userId} AND Status = {status}";
string sql = query.GetQuery();
// Result: "SELECT * FROM Users WHERE Id = @p1 AND Status = @p2"
var parameters = query.GetParameters();
// Result: { ["p1"] = userId, ["p2"] = status }
var userId = 123;
var status = "Active";
SqlQueryBuilder queryBuilder = $"SELECT * FROM Orders WHERE UserId = {userId} AND Status = {status}";
var (query, parameters) = queryBuilder.GetQueryAndParameters();
// query: "SELECT * FROM Orders WHERE UserId = @p1 AND Status = @p2"
// parameters: { ["p1"] = 123, ["p2"] = "Active" }
Easily compose complex queries by combining multiple SqlQueryBuilder instances. Parameter names are automatically managed to avoid collisions:
SqlQueryBuilder innerQuery = $"SELECT OrderId FROM Orders WHERE UserId = {userId}";
SqlQueryBuilder outerQuery = $"SELECT * FROM OrderDetails WHERE OrderId IN ({innerQuery})";
var (sql, parameters) = outerQuery.GetQueryAndParameters();
// sql: "SELECT * FROM OrderDetails WHERE OrderId IN (SELECT OrderId FROM Orders WHERE UserId = @p1)"
// parameters: { ["p1"] = userId }
// Even with multiple nested queries, parameter names remain unique
SqlQueryBuilder innermost = $"SELECT CustomerId FROM Customers WHERE Region = {region}";
SqlQueryBuilder middle = $"SELECT OrderId FROM Orders WHERE CustomerId IN ({innermost}) AND Status = {status}";
SqlQueryBuilder outermost = $"SELECT * FROM OrderDetails WHERE OrderId IN ({middle}) AND Quantity > {minQuantity}";
var (complexSql, complexParams) = outermost.GetQueryAndParameters();
// Parameters: { ["p1"] = region, ["p2"] = status, ["p3"] = minQuantity }
// All parameter names are automatically managed and collision-safe
Use the :l format specifier to include values as literal strings (use with caution):
var tableName = "Orders";
var userId = 123;
SqlQueryBuilder query = $"SELECT * FROM {tableName:l} WHERE UserId = {userId}";
var (sql, parameters) = query.GetQueryAndParameters();
// sql: "SELECT * FROM Orders WHERE UserId = @p1"
// parameters: { ["p1"] = 123 }
Customize parameter names for better integration with your ORM:
SqlQueryBuilder query = $"SELECT * FROM Users WHERE Id = {userId}";
var (sql, parameters) = query.GetQueryAndParameters("param");
// sql: "SELECT * FROM Users WHERE Id = @param1"
// parameters: { ["param1"] = userId }
Combine multiple query parts using standard string concatenation. Parameter names remain unique across all parts:
SqlQueryBuilder baseQuery = $"SELECT * FROM Orders WHERE UserId = {userId}";
SqlQueryBuilder extendedQuery = baseQuery + $" AND CreatedDate > {startDate}";
var (sql, parameters) = extendedQuery.GetQueryAndParameters();
// sql: "SELECT * FROM Orders WHERE UserId = @p1 AND CreatedDate > @p2"
// parameters: { ["p1"] = userId, ["p2"] = startDate }
// Multiple concatenations maintain parameter uniqueness
SqlQueryBuilder furtherExtended = extendedQuery + $" AND Amount > {minAmount}";
// Parameters: { ["p1"] = userId, ["p2"] = startDate, ["p3"] = minAmount }
Attach additional metadata to your queries for logging, caching, or other purposes:
var queryBuilder = ((SqlQueryBuilder)$"SELECT * FROM Orders WHERE Id = {orderId}")
.AddMetadata("Operation", "GetOrder")
.AddMetadata("CacheKey", $"order_{orderId}");
var metadata = queryBuilder.Metadata;
// metadata: { ["Operation"] = "GetOrder", ["CacheKey"] = "order_123" }
var minAmount = 1000;
var status = "Completed";
SqlQueryBuilder queryBuilder = $"""
SELECT o.*, c.Name as CustomerName
FROM Orders o
INNER JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Amount >= {minAmount} AND o.Status = {status}
""";
var (query, parameters) = queryBuilder.GetQueryAndParameters();
var orders = context.Orders
.FromSqlRaw(query, parameters.Values.ToArray())
.ToList();
var queryBuilder = $"SELECT * FROM Products WHERE CategoryId = {categoryId} AND Price > {minPrice}";
var (query, parameters) = queryBuilder.GetQueryAndParameters();
var products = connection.Query<Product>(
query,
parameters
);
SqlQueryBuilder automatically protects against SQL injection by parameterizing all interpolated values. Values are never directly concatenated into the SQL string, ensuring your queries are safe by default.
var userInput = "'; DROP TABLE Users; --";
SqlQueryBuilder query = $"SELECT * FROM Users WHERE Name = {userInput}";
// Safe! Results in: "SELECT * FROM Users WHERE Name = @p1"
// Parameter: { ["p1"] = "'; DROP TABLE Users; --" }
| Method | Description |
|---|---|
GetQuery() | Returns the parameterized SQL query string |
GetParameters() | Returns the parameter dictionary with auto-generated, collision-safe names |
GetQueryAndParameters() | Returns both query and parameters as a tuple |
GetQueryAndParameters(string prefix) | Returns query and parameters with custom parameter prefix |
AddMetadata(string key, object? value) | Adds metadata to the query builder |
SqlQueryBuilder automatically generates parameter names using a sequential numbering system (@p1, @p2, etc.) that ensures:
GetQueryAndParameters(prefix) to customize the parameter name prefix| Format | Description | Example |
|---|---|---|
| Default | Parameterized value | {userId} → @p1 |
:l | Literal string | {tableName:l} → TableName |
:p | Explicit parameter | {value:p} → @p1 |
This project is licensed under the MIT License - see the LICENSE.md file for details.