Carrigan.SqlTools
0.5.2
See the version list below for details.
dotnet add package Carrigan.SqlTools --version 0.5.2
NuGet\Install-Package Carrigan.SqlTools -Version 0.5.2
<PackageReference Include="Carrigan.SqlTools" Version="0.5.2" />
<PackageVersion Include="Carrigan.SqlTools" Version="0.5.2" />
<PackageReference Include="Carrigan.SqlTools" />
paket add Carrigan.SqlTools --version 0.5.2
#r "nuget: Carrigan.SqlTools, 0.5.2"
#:package Carrigan.SqlTools@0.5.2
#addin nuget:?package=Carrigan.SqlTools&version=0.5.2
#tool nuget:?package=Carrigan.SqlTools&version=0.5.2
Carrigan.SqlTools
Carrigan.SqlTools is a .NET library that simplifies SQL generation for Microsoft SQL Server, while still giving you control when you need it.
It automatically generates SELECT, INSERT, UPDATE, and DELETE statements using reflection. Carrigan.SqlTools adds a safe, object-oriented API for building more advanced queries.
A companion library, Carrigan.SqlTools.SqlServer, extends functionality by wrapping ADO.NET to execute generated queries, map rows to objects, and handle decryption of encrypted properties.
The transient dependency Carrigan.Core provides interfaces and property attributes to integrate your custom property-level encryption, and other miscellaneous "helper" dependencies.
This package includes Roslyn analyzers bundled with the package to assist with safe usage patterns.
SQL Server is a trademark of Microsoft Corporation. This project is not affiliated with or endorsed by Microsoft.
This library may generate or execute SQL. Review generated SQL before use in production systems. Always test against non-production databases first.
Use caution with schema, migration, and data-modifying operations. The authors are not responsible for data loss, downtime, or unintended database changes.
Table of Contents
- Features
- Installation
- Getting Started Examples
- More Complex Examples
- Attribute Examples
- Running Queries (Async & Non-Async)
- Simple ADO.NET Example With SqlQuery
- Data Type Mappings
- ExampleEncryptor (AesGcm-based) and IDecrypters
- License
Features
Automatic CRUD generation
BuildSELECT,INSERT,UPDATE, andDELETEqueries with reflection.Carrigan.Core integration
Interfaces and property-level attributes enable property-level encryption and decryption.Manual query builder
Safely construct advanced SQL withJOIN, predicates (e.g.,Equal,GreaterThan,IsNull,Like,And,Or,Xor,Not),ORDER BY, and pagination (OFFSET/FETCH NEXT).Dictionary → object mapping
Use the invocation system to populate typed models from database rows.Focused on SQL Server
SQL output targets Microsoft SQL Server.Execution helpers
Async commands (CommandsAsync) and non-async commands (Commands) for running queries and reading results.
Installation
Carrigan.SqlTools is available as a NuGet package:
dotnet add package Carrigan.SqlTools
For SQL Server execution helpers:
dotnet add package Carrigan.SqlTools.SqlServer
Getting Started Examples
We use SqlGenerator<T> to produce a SqlQuery (with QueryText, CommandType, and Parameters).
All examples use the following using statements to keep code clean in the examples.
using Carrigan.SqlTools.Attributes;
using Carrigan.SqlTools.Sets;
using Carrigan.SqlTools.SqlGenerators;
using Carrigan.SqlTools.Tests.TestEntities;
// Example data models
public class Customer
{
[PrimaryKey] //note: PrimaryKey take precedence over key for the Sql Generator
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public string Phone { get; set; } = "";
}
public class Order
{
[PrimaryKey] //Required attribute for certain SQL Generations
public int Id { get; set; }
public int CustomerId { get; set; }
public int PaymentMethodId { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
}
// Generators
public SqlGenerator<Customer> customerGenerator = new();
Select All Rows
SqlQuery query = customerGenerator.SelectAll();
// SELECT [Customer].* FROM [Customer]
Select by Id
Key attribute required, and composite keys are supported by specifying multiple Keys.
Customer entity = new() { Id = 42 };
SqlQuery query = customerGenerator.SelectById(entity);
// SELECT [Customer].*
// FROM [Customer]
// WHERE ([Customer].[Id] = @Parameter_Id)
Insert
Customer entity = new()
{
Id = 42,
Name = "Hank",
Email = "Hank@example.com",
Phone = "+1(555)555-5555"
};
SqlQuery query = customerGenerator.Insert(null, null, entity);
// INSERT INTO [Customer] ([Id], [Name], [Email], [Phone])
// VALUES (@Id, @Name, @Email, @Phone);
Insert with Auto Id
Key attribute required, and Id columns must have a default value.
Customer entity = new()
{
Name = "Hank",
Email = "Hank@example.com",
Phone= "+1(555)555-5555"
};
SqlQuery query = customerGenerator.InsertAutoId(entity);
// DECLARE @OutputTable TABLE (InsertedId INT);
// INSERT INTO [Customer] ([Name], [Email], [Phone])
// VALUES (@Name, @Email, @Phone);
// OUTPUT INSERTED.Id INTO @OutputTable
// VALUES (@Name, @Email, @Phone);
// SELECT Id FROM @OutputTable;
Update by Id
Key attribute required, and composite keys are supported by specifying multiple Keys.
Customer entity = new()
{
Id = 42,
Name = "Hank",
Email = "Hank@example.com",
Phone = "+1(555)555-5555"
};
SqlQuery query = customerGenerator.UpdateById(entity);
// UPDATE [Customer]
// SET [Name] = @Name, [Email] = @Email, [Phone] = @Phone
// WHERE [Id] = @Id;
Update by Id (selected columns)
Key attribute required, and composite keys are supported by specifying multiple Keys.
ColumnCollection<T> validates the names of the properties, and throws an error if the property isn't valid
ColumnCollection<Customer> columns = new(nameof(Customer.Email));
Customer entity = new() { Id = 42, Name = "Hank", Email = "Hank@example.gov" };
SqlQuery query = customerGenerator.UpdateById(entity, columns);
// UPDATE [Customer] SET [Email] = @Email WHERE [Id] = @Id;
Delete
Key attribute required, and composite keys are supported by specifying multiple Keys.
Customer entity = new() { Id = 42 };
SqlQuery query = customerGenerator.Delete(entity);
// DELETE FROM [Customer] WHERE [Id] = @Id;
Delete by Id (multiple keys)
Key attribute required, and composite keys are supported by specifying multiple Keys.
Customer[] entities = [new() { Id = 1 }, new() { Id = 2 }];
SqlQuery query = customerGenerator.DeleteById(entities);
// DELETE FROM [Customer]
// WHERE (([Customer].[Id] = @Parameter_0_R_Id) OR ([Customer].[Id] = @Parameter_1_R_Id))
More Complex Examples
We use SqlGenerator<T> to produce a SqlQuery (with QueryText, CommandType, and Parameters).
All examples use the following using statements to keep code clean in the examples.
using Carrigan.SqlTools.JoinTypes;
using Carrigan.SqlTools.OrderByItems;
using Carrigan.SqlTools.PredicatesLogic;
using Carrigan.SqlTools.Sets;
using Carrigan.SqlTools.SqlGenerators;
// Example data models
public class Customer
{
[PrimaryKey] //note: PrimaryKey take precedence over key for the Sql Generator
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public string Phone { get; set; } = "";
}
public class Order
{
[PrimaryKey] //Required attribute for certain SQL Generations
public int Id { get; set; }
public int CustomerId { get; set; }
public int PaymentMethodId { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
}
// Generators
public SqlGenerator<Customer> customerGenerator = new();
Select with Joins and Order By
ColumnEqualsColumn<LeftT, RightT>, Order validates the names of the properties, and throws an error if the property isn't valid
OrderByItem<Order> validates the names of the properties, and throws an error if the property isn't valid
ColumnEqualsColumn<Customer, Order> predicate = new(nameof(Customer.Id), nameof(Order.CustomerId));
Joins<Customer> join = Joins<Customer>.InnerJoin<Order>(predicate);
OrderByItem<Order> orderByOrderDate = new(nameof(Order.OrderDate));
SqlQuery query = customerGenerator.Select(null, join, null, orderByOrderDate, null);
// SELECT [Customer].*
// FROM [Customer]
// INNER JOIN [Order]
// ON ([Customer].[Id] = [Order].[CustomerId])
// ORDER BY [Order].[OrderDate] ASC
Select with Two Part Order By
ColumnEqualsColumn<LeftT, RightT>, Order validates the names of the properties, and throws an error if the property isn't valid
OrderByItem<Order> validates the names of the properties, and throws an error if the property isn't valid
ColumnEqualsColumn<Customer, Order> predicate = new(nameof(Customer.Id), nameof(Order.CustomerId));
Joins<Customer> join = Joins<Customer>.InnerJoin<Order>(predicate);
OrderByItem<Order> orderByOrderDate = new(nameof(Order.OrderDate));
OrderByItem<Customer> orderByCustomerId = new(nameof(Customer.Id), SortDirectionEnum.Descending);
OrderBy orderBy = new(orderByCustomerId, orderByOrderDate);
SqlQuery query = customerGenerator.Select(null, join, null, orderBy, null);
// SELECT [Customer].*
// FROM [Customer]
// INNER JOIN [Order]
// ON ([Customer].[Id] = [Order].[CustomerId])
// ORDER BY [Customer].[Id] DESC, [Order].[OrderDate] ASC"
Delete with Join and Where
ColumnEqualsColumn<LeftT, RightT> validates the names of the properties, and throws an error if the property isn't valid
ColumnValues<T> validates the names of the properties, and throws an error if the property isn't valid
ColumnEqualsColumn<Customer, Order> predicate = new(nameof(Customer.Id), nameof(Order.CustomerId));
Joins<Order> join = Joins<Order>.InnerJoin<Customer>(predicate);
ColumnValue<Customer> customerEmail = new(nameof(Customer.Email), "spam@example.com");
SqlQuery query = orderGenerator.Delete(join, customerEmail);
// DELETE [Order]
// FROM [Order]
// INNER JOIN [Customer]
// ON ([Customer].[Id] = [Order].[CustomerId])
// WHERE ([Customer].[Email] = @Parameter_Email)
Select Count With Where
Columns<T> validates the names of the properties, and throws an error if the property isn't valid
Column<Order> totalCol = new (nameof(Order.Total));
Parameter minTotal = new ("Total", 500m);
GreaterThan greaterThan = new (totalCol, minTotal);
SqlQuery query = orderGenerator.SelectCount(null, null, greaterThan);
// SELECT COUNT([Order].*)
// FROM [Order]
// WHERE ([Order].[Total] > @Parameter_Total)
Update with Joins and Where
ColumnCollection<T> validates the names of the properties, and throws an error if the property isn't valid
ColumnEqualsColumn<LeftT, RightT> validates the names of the properties, and throws an error if the property isn't valid
ColumnValues<T> validates the names of the properties, and throws an error if the property isn't valid
Order entity = new () { Id = 10, Total = 123.45m };
ColumnCollection<Order> columnCollection = new(nameof(Order.Total));
ColumnEqualsColumn<Order, Customer> predicate = new(nameof(Order.CustomerId), nameof(Customer.Id));
Joins<Order> join = Joins<Order>.InnerJoin<Customer>(predicate);
ColumnValue<Customer> customerEmailEquals = new(nameof(Customer.Email), "spam@example.com");
SqlQuery query = orderGenerator.Update(entity, columnCollection, join, customerEmailEquals);
// UPDATE [Order]
// SET [Order].[Total] = @ParameterSet_Total
// FROM [Order]
// INNER JOIN [Customer]
// ON ([Order].[CustomerId] = [Customer].[Id])
// WHERE ([Customer].[Email] = @Parameter_Email)
Attribute Examples
We use SqlGenerator<T> to produce a SqlQuery (with QueryText, CommandType, and Parameters).
You can use the [Table] attribute from System.ComponentModel.DataAnnotations.Schema to override the table name, otherwise the table name is assumed to be the same as the class. You can also specify the schema name or not.
All examples use using statements to keep code clean, and initialize generators with an encryptor (required).
using Carrigan.SqlTools.SqlGenerators;
using Carrigan.SqlTools.Attributes;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// Generators
private static readonly SqlGenerator<PhoneModel> phoneGenerator = new();
private static readonly SqlGenerator<EmailModel> emailGenerator = new();
private static readonly SqlGenerator<ProcedureExec> procedureExecGenerator = new();
Table, Column and Key
[Table("Phone", Schema="schema")]
internal class PhoneModel
{
[Key]
public int Id { get; set; }
public int CustomerId { get; set; }
[Column("Phone")]
public string? PhoneNumber { get; set; }
}
PhoneModel phone = new()
{
Id = 2718,
CustomerId = 3141,
PhoneNumber = "07700 900461"
};
SqlQuery query = phoneGenerator.UpdateById(phone);
// UPDATE [schema].[Phone]
// SET [CustomerId] = @CustomerId, [Phone] = @Phone
// WHERE [Id] = @Id;
Identifier and Primary Key
[Identifier("Email", "schema")]
internal class EmailModel
{
[PrimaryKey]
public int Id { get; set; }
public int CustomerId { get; set; }
[Identifier("Email")]
public string? EmailAddress { get; set; }
}
EmailModel email = new()
{
Id = 10,
CustomerId = 313,
EmailAddress = "Exterminate@GenericTinCanLand.gov"
};
SqlQuery query = emailGenerator.UpdateById(email);
// UPDATE [schema].[Email]
// SET [CustomerId] = @CustomerId, [Email] = @Email
// WHERE [Id] = @Id;
Procedure and Parameter
[Identifier("UpdateThing", "schema")]
internal class ProcedureExec
{
[Parameter ("SomeValue")]
public string? ValueColumn { get; set; }
}
ProcedureExec procedureExec = new()
{
ValueColumn = "DangIt"
};
SqlQuery query = procedureExecGenerator.Procedure(procedureExec);
// [schema].[UpdateThing]
Running Queries (Async & Non-Async)
The SQL Server helpers live in Carrigan.SqlTools.SqlServer.
These examples use using statements to keep code tidy.
using System.Collections.Generic;
using System.Data.Common;
using Carrigan.Core.Interfaces;
using Carrigan.SqlTools.SqlGenerators;
using Carrigan.SqlTools.SqlServer;
using Microsoft.Data.SqlClient;
Async: ExecuteNonQueryAsync / ExecuteScalarAsync / ExecuteReaderAsync<T>
// Build a query (example: SelectAll)
SqlQuery query = customerGenerator.SelectAll();
SqlConnection connection = new SqlConnection(<your connection string>);
DbTransaction transaction = null;
// Implement IDecrypters (see ExampleEncryptor section)
IDecrypters decrypters = new MyDecrypters();
// Execute (async)
IEnumerable<Customer> rows =
await CommandsAsync.ExecuteReaderAsync<Customer>(query, transaction, connection, decrypters);
// Write/update (async)
int affected =
await CommandsAsync.ExecuteNonQueryAsync(query, transaction, connection);
// Single scalar (async)
object result =
await CommandsAsync.ExecuteScalarAsync(query, transaction, connection);
Example Encryptor (AesGcm-based) and IDecrypters
Non-Async: ExecuteNonQuery / ExecuteScalar / ExecuteReader<T>
Customer[] toDelete = new Customer[] { new Customer { Id = 7 } };
SqlQuery query = customerGenerator.DeleteById(toDelete);
SqlConnection connection = new SqlConnection("Server=.;Database=AppDb;Integrated Security=true;");
DbTransaction transaction = null;
// IDecrypters (same interface as async)
IDecrypters decrypters = new MyDecrypters();
// Execute (sync)
int affected = Commands.ExecuteNonQuery(query, transaction, connection);
object scalar = Commands.ExecuteScalar(query, transaction, connection);
IEnumerable<Customer> customers =
Commands.ExecuteReader<Customer>(query, transaction, connection, decrypters);
Simple ADO.NET Example With SqlQuery
If you prefer raw ADO.NET, you can still use SqlQuery for the SQL text, command type, and parameters:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Carrigan.SqlTools.SqlGenerators;
using Microsoft.Data.SqlClient;
// 1) Generate the query
SqlQuery query = customerGenerator.SelectById(new Customer { Id = 42 });
// 2) Use ADO.NET directly
SqlConnection connection = new SqlConnection("Server=.;Database=AppDb;Integrated Security=true;");
connection.Open();
DbCommand command = connection.CreateCommand();
command.CommandText = query.QueryText; // ← from SqlQuery
command.CommandType = query.CommandType; // ← from SqlQuery
// Add parameters
foreach (KeyValuePair<string, object?> p in query.Parameters)
{
command.Parameters.Add(new SqlParameter(p.Key, p.Value ?? DBNull.Value));
}
// Execute
DbDataReader reader = command.ExecuteReader();
// ... map rows (or use invoker) ...
reader.Close();
connection.Close();
Data Type Mappings
Default Parameter Return Types
| C# CLR Type | Default SqlDbType |
|---|---|
| Guid | UniqueIdentifier |
| string | NVarChar |
| char | NChar |
| byte[] | VarBinary |
| bool | Bit |
| byte | TinyInt |
| sbyte | SmallInt |
| short | SmallInt |
| int | Int |
| long | BigInt |
| float | Real |
| double | Float |
| decimal | Decimal |
| DateTime | DateTime2 |
| DateOnly | Date |
| TimeOnly | Time |
| DateTimeOffset | DateTimeOffset |
| XDocument | XML |
| XmlDocument | XML |
Allowed Override Types
| C# CLR Type | Allowed SqlTypeAttribute-derived attributes | SqlDbTypes allowed by those attributes |
|---|---|---|
| byte[] | SqlBinaryAttribute<br>SqlVarBinaryMaxAttribute | SqlDbType.Binary<br>SqlDbType.VarBinary |
| char | SqlCharAttribute | SqlDbType.Char<br>SqlDbType.NChar<br>SqlDbType.NVarChar<br>SqlDbType.VarChar |
| string | SqlCharAttribute<br>SqlVarCharMaxAttribute | SqlDbType.Char<br>SqlDbType.NChar<br>SqlDbType.NVarChar<br>SqlDbType.VarChar |
| DateTime | SqlDateTime2Attribute | SqlDbType.DateTime2 |
| DateOnly | SqlDateAttribute | SqlDbType.Date |
| TimeOnly | SqlTimeAttribute | SqlDbType.Time |
| DateTimeOffset | SqlDateTimeOffsetAttribute | SqlDbType.DateTimeOffset |
| double | SqlFloatAttribute | SqlDbType.Float |
| decimal | SqlDecimalAttribute | SqlDbType.Decimal |
Allowed Override Types With Warnings
| C# CLR Type | Allowed SqlTypeAttribute-derived attributes | SqlDbTypes allowed by those attributes |
|---|---|---|
| byte[] | SqlImageAttribute | SqlDbType.Image |
| char | SqlVarCharMaxAttribute<br>SqlTextAttribute | SqlDbType.VarChar<br>SqlDbType.NVarChar<br>SqlDbType.Text<br>SqlDbType.NText |
| string | SqlTextAttribute | SqlDbType.Text<br>SqlDbType.NText |
| DateTime | SqlDateTimeAttribute<br>SqlDateAttribute<br>SqlTimeAttribute | SqlDbType.DateTime<br>SqlDbType.SmallDateTime<br>SqlDbType.Date<br>SqlDbType.Time |
| DateOnly | SqlDateTimeAttribute<br>SqlDateTime2Attribute | SqlDbType.DateTime<br>SqlDbType.SmallDateTime<br>SqlDbType.DateTime2 |
| TimeOnly | SqlDateTimeAttribute<br>SqlDateTime2Attribute | SqlDbType.DateTime<br>SqlDbType.SmallDateTime<br>SqlDbType.DateTime2 |
| float | SqlFloatAttribute<br>SqlDecimalAttribute<br>SqlMoneyAttribute | SqlDbType.Float<br>SqlDbType.Decimal<br>SqlDbType.Money<br>SqlDbType.SmallMoney |
| double | SqlDecimalAttribute<br>SqlMoneyAttribute | SqlDbType.Decimal<br>SqlDbType.Money<br>SqlDbType.SmallMoney |
| decimal | SqlFloatAttribute<br>SqlMoneyAttribute | SqlDbType.Float<br>SqlDbType.Money<br>SqlDbType.SmallMoney |
ExampleEncryptor (AesGcm-based) and IDecrypters
Below is an example encryptor that uses an AesGcm-style API. Replace key handling with your own secure storage.
//THIS IS JUST A SIMPLE EXAMPLE OF A ENCRYPTION CLASS
//I AM NOT A CRYPTOGRAPHIC EXPERT, DO NOT USE THIS EXAMPLE IN A REAL SYSTEM.
using System;
using System.Text;
using System.Security.Cryptography;
using Carrigan.Core.Interfaces;
public sealed class ExampleNonceGenerator : INonceGenerator
{
public byte[] GenerateNonce()
{
byte[] nonce = new byte[12]; // 96-bit nonce
RandomNumberGenerator.Fill(nonce);
return nonce;
}
}
public sealed class ExampleEncryptor : IEncryption
{
private readonly byte[] _key;
private const int TagSize = 16;
public int? Version => 1;
public byte[] KeyBytes => _key;
public ExampleEncryptor(byte[] key)
{
if (key == null || key.Length != 32) throw new ArgumentException("Key must be 32 bytes.");
_key = key;
}
public string? Encrypt(string? plainText)
{
if (plainText == null) return null;
byte[] pt = Encoding.UTF8.GetBytes(plainText);
byte[] ct = new byte[pt.Length];
byte[] nonce = new byte[12];
byte[] tag = new byte[TagSize];
RandomNumberGenerator.Fill(nonce);
using (AesGcm gcm = new AesGcm(_key, TagSize))
{
gcm.Encrypt(nonce, pt, ct, tag);
}
byte[] combined = new byte[nonce.Length + ct.Length + tag.Length];
Buffer.BlockCopy(nonce, 0, combined, 0, nonce.Length);
Buffer.BlockCopy(ct, 0, combined, nonce.Length, ct.Length);
Buffer.BlockCopy(tag, 0, combined, nonce.Length + ct.Length, tag.Length);
return Convert.ToBase64String(combined);
}
public string? Decrypt(string? cipherText)
{
if (cipherText == null) return null;
byte[] combined = Convert.FromBase64String(cipherText);
byte[] nonce = new byte[12];
byte[] tag = new byte[TagSize];
int cipherLen = combined.Length - nonce.Length - tag.Length;
byte[] ct = new byte[cipherLen];
Buffer.BlockCopy(combined, 0, nonce, 0, nonce.Length);
Buffer.BlockCopy(combined, nonce.Length, ct, 0, ct.Length);
Buffer.BlockCopy(combined, nonce.Length + ct.Length, tag, 0, tag.Length);
byte[] pt = new byte[ct.Length];
using (AesGcm gcm = new AesGcm(_key, TagSize))
{
gcm.Decrypt(nonce, ct, tag, pt);
}
return Encoding.UTF8.GetString(pt);
}
}
// Minimal IDecrypters imply mapping versions → encrypters
//THIS IS JUST A SIMPLE EXAMPLE OF A ENCRYPTION CLASS
//I AM NOT A CRYPTOGRAPHIC EXPERT, DO NOT USE THIS EXAMPLE IN A REAL SYSTEM.
public sealed class MyDecrypters : IDecrypters
{
private readonly System.Collections.Generic.Dictionary<int, IEncryption> _map =
new System.Collections.Generic.Dictionary<int, IEncryption>()
{
{ 1, new ExampleEncryptor(RandomNumberGenerator.GetBytes(32)) }
};
public System.Collections.Generic.IEnumerable<int> Keys => _map.Keys;
public IEncryption? Decryptor(int version)
{
return _map.TryGetValue(version, out IEncryption enc) ? enc : null;
}
}
//THIS IS JUST A SIMPLE EXAMPLE OF A ENCRYPTION CLASS
//I AM NOT A CRYPTOGRAPHIC EXPERT, DO NOT USE THIS EXAMPLE IN A REAL SYSTEM.
Plug
MyDecryptersintoExecuteReaderAsync<T>/ExecuteReader<T>to transparently decrypt properties annotated in your models.
License
Carrigan.SqlTools
Copyright © 2025 Carrigan Software Solutions LLC
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
Disclaimers
SQL Server is a trademark of Microsoft Corporation. This project is not affiliated with or endorsed by Microsoft.
This library may generate or execute SQL. Review generated SQL before use in production systems. Always test against non-production databases first.
Use caution with schema, migration, and data-modifying operations. The authors are not responsible for data loss, downtime, or unintended database changes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 is compatible. 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. |
-
net10.0
- Carrigan.Core (>= 0.1.4)
-
net9.0
- Carrigan.Core (>= 0.1.4)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Carrigan.SqlTools:
| Package | Downloads |
|---|---|
|
Carrigan.SqlTools.Clients.Core
Shared core client infrastructure for Carrigan.SqlTools database dialect packages. Intended primarily as a transitive dependency for dialect-specific client implementations. |
|
|
Carrigan.SqlTools.Generators.PostgreSql
Carrigan.SqlTools.Generators.PostgreSql provides PostgreSQL-specific SQL generation support for Carrigan.SqlTools. The package extends the core Carrigan.SqlTools query-building model with PostgreSQL dialect rules, including PostgreSQL identifier formatting, parameter formatting, data type generation, LIMIT/OFFSET paging syntax, joins, WHERE clauses, ORDER BY clauses, and CRUD statement generation. It supports reflection-based generation of SELECT, INSERT, UPDATE, and DELETE statements while preserving the flexibility to build queries manually through a strongly typed, object-oriented SQL construction model. Carrigan.SqlTools.Generators.PostgreSql is intended for developers who need safe, reusable PostgreSQL query generation without adopting a full ORM. It works with Carrigan.Core attributes and shared interfaces to support property-level metadata, custom encryption workflows, object mapping, and consistent SQL generation across application code. This package focuses specifically on PostgreSQL dialect behavior and is designed to be used alongside the Carrigan.SqlTools core abstractions. |
|
|
Carrigan.SqlTools.Generators.SqlServer
Carrigan.SqlTools.Generators.SqlServer provides Microsoft SQL Server-specific SQL generation support for Carrigan.SqlTools. Its primary purpose is to generate SELECT, INSERT, UPDATE, and DELETE statements using reflection and the shared Carrigan.SqlTools query model. The package integrates with Carrigan.Core through shared interfaces and property-level metadata used by the Carrigan.SqlTools model. In addition to automated generation, the SQL Server generator supports a manual, object-oriented approach for building SQL Server statements with ORDER BY, JOIN, WHERE clauses, and paginated queries. The generated SQL can be used with the dialect client packages, which use shared invocation infrastructure to map result rows to model instances. This package focuses specifically on SQL Server dialect behavior. The companion package Carrigan.SqlTools.Clients.SqlServer provides ADO.NET execution helpers for generated SQL Server queries. |
|
|
Carrigan.SqlTools.Clients.PostgreSql
PostgreSQL client support for Carrigan.SqlTools. This package provides PostgreSQL-specific client functionality for executing Carrigan.SqlTools-generated SQL against PostgreSQL databases, using the shared Carrigan.SqlTools client core infrastructure. |
|
|
Carrigan.SqlTools.Clients.SqlServer
Carrigan.SqlTools.Clients.SqlServer is a companion library to Carrigan.SqlTools.Generators.SqlServer, focused on executing SQL Server queries at runtime. It provides a wrapper around ADO.NET that makes it easier to run the queries generated by Carrigan.SqlTools.Generators.SqlServer. It also uses the shared invocation infrastructure to create model instances from SQL Server result rows. In addition, the library handles decryption of encrypted properties when encryption logic has been defined through the interfaces and attributes provided by Carrigan.Core. Together with Carrigan.SqlTools.Generators.SqlServer, this library provides a full workflow for generating, executing, and consuming SQL Server queries with object-oriented protections. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.7.0 | 55 | 7/25/2026 |
| 0.7.0-alpha.202607240141 | 57 | 7/24/2026 |
| 0.7.0-alpha.202607232350 | 52 | 7/23/2026 |
| 0.7.0-alpha.202607230225 | 61 | 7/23/2026 |
| 0.7.0-alpha.202607200640 | 83 | 7/20/2026 |
| 0.7.0-alpha.202607170545 | 85 | 7/17/2026 |
| 0.6.1 | 225 | 6/11/2026 |
| 0.6.0 | 186 | 6/8/2026 |
| 0.6.0-alpha.202606080807 | 88 | 6/8/2026 |
| 0.5.3-alpha.202606110305 | 67 | 6/11/2026 |
| 0.5.2 | 121 | 6/11/2026 |
| 0.5.2-alpha.202605200417 | 61 | 5/20/2026 |
| 0.5.1 | 70 | 5/20/2026 |
| 0.5.1-alpha.202604241732 | 63 | 4/24/2026 |
| 0.5.0 | 74 | 4/24/2026 |
| 0.5.0-alpha.202604241029 | 65 | 4/24/2026 |