GennadyGS.SqlQueryBuilder 1.5.0

dotnet add package GennadyGS.SqlQueryBuilder --version 1.5.0
                    
NuGet\Install-Package GennadyGS.SqlQueryBuilder -Version 1.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GennadyGS.SqlQueryBuilder" Version="1.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GennadyGS.SqlQueryBuilder" Version="1.5.0" />
                    
Directory.Packages.props
<PackageReference Include="GennadyGS.SqlQueryBuilder" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GennadyGS.SqlQueryBuilder --version 1.5.0
                    
#r "nuget: GennadyGS.SqlQueryBuilder, 1.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GennadyGS.SqlQueryBuilder@1.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GennadyGS.SqlQueryBuilder&version=1.5.0
                    
Install as a Cake Addin
#tool nuget:?package=GennadyGS.SqlQueryBuilder&version=1.5.0
                    
Install as a Cake Tool

SqlQueryBuilder

NuGet License: MIT .NET GitHub stars

A powerful, type-safe SQL query builder for .NET that leverages C# string interpolation to create parameterized SQL queries with automatic SQL injection protection.

Features

  • SQL Injection Protection: Automatically parameterizes interpolated values
  • Type-Safe: Full IntelliSense support with compile-time checking
  • Simple API: Natural C# string interpolation syntax
  • Composable: Easily combine and nest query builders
  • Auto-Generated Parameters: Parameter names are automatically generated and collision-safe, even in complex query composition scenarios
  • Flexible Formatting: Support for literal strings and custom parameter names
  • Metadata Support: Attach additional information to queries
  • High Performance: Minimal overhead with efficient string building
  • Zero Dependencies: Lightweight library with no external dependencies

Quick Start

Installation

Install via NuGet Package Manager:

Install-Package GennadyGS.SqlQueryBuilder

Or via .NET CLI:

dotnet add package GennadyGS.SqlQueryBuilder

Basic Usage

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 }

Examples

Basic Query Building

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" }

Query Composition

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

Literal String Formatting

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 }

Custom Parameter Prefixes

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 }

Query Concatenation

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 }

Metadata Support

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" }

Working with ORMs

Entity Framework Core
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();
Dapper
var queryBuilder = $"SELECT * FROM Products WHERE CategoryId = {categoryId} AND Price > {minPrice}";

var (query, parameters) = queryBuilder.GetQueryAndParameters();
var products = connection.Query<Product>(
    query,
    parameters
);

Security

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; --" }

Supported Frameworks

  • NET 8.0 and later

API Reference

Core Methods

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

Parameter Name Generation

SqlQueryBuilder automatically generates parameter names using a sequential numbering system (@p1, @p2, etc.) that ensures:

  • Collision Safety: No duplicate parameter names, even in complex compositions
  • Predictable Ordering: Parameters are numbered in the order they appear
  • Composition Support: Nested and concatenated queries maintain unique parameter names
  • Custom Prefixes: Use GetQueryAndParameters(prefix) to customize the parameter name prefix

Format Specifiers

Format Description Example
Default Parameterized value {userId}@p1
:l Literal string {tableName:l}TableName
:p Explicit parameter {value:p}@p1

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Acknowledgments

  • Inspired by the need for type-safe SQL query building in .NET
  • Built with modern C# interpolated string handlers
  • Designed for developer productivity and application security
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.5.0 129 7/15/2025
1.4.1 272 12/9/2023
1.4.0 147 12/9/2023
1.3.1 249 4/29/2023
1.2.2 392 11/9/2022
1.2.1 560 8/4/2022
1.1.1 457 8/2/2022
1.0.5 460 8/1/2022
1.0.4 474 3/26/2022
1.0.3 489 2/7/2022
1.0.2 501 1/22/2022
1.0.1 513 1/16/2022
1.0.0 536 1/15/2022