SujaySarma.Data.SqlServer
10.0.0
dotnet add package SujaySarma.Data.SqlServer --version 10.0.0
NuGet\Install-Package SujaySarma.Data.SqlServer -Version 10.0.0
<PackageReference Include="SujaySarma.Data.SqlServer" Version="10.0.0" />
<PackageVersion Include="SujaySarma.Data.SqlServer" Version="10.0.0" />
<PackageReference Include="SujaySarma.Data.SqlServer" />
paket add SujaySarma.Data.SqlServer --version 10.0.0
#r "nuget: SujaySarma.Data.SqlServer, 10.0.0"
#:package SujaySarma.Data.SqlServer@10.0.0
#addin nuget:?package=SujaySarma.Data.SqlServer&version=10.0.0
#tool nuget:?package=SujaySarma.Data.SqlServer&version=10.0.0
SujaySarma.Data.SqlServer
ORM and fluent SQL builder for SQL Server with full T-SQL feature parity, without Entity Framework complexity.
Overview
SujaySarma.Data.SqlServer provides a lightweight, high-performance ORM for SQL Server that focuses on T-SQL feature parity over abstraction. Build complex queries using fluent, type-safe builders or execute raw SQL with full control. Works with on-prem SQL Server, MSDE, LocalDB, named instances, SQL Azure, and SQL on Linux.
This library builds on SujaySarma.Data.Core and aims to provide everything possible with T-SQL through an intuitive API.
Installation
$ dotnet add package SujaySarma.Data.SqlServer
NuGet Package: SujaySarma.Data.SqlServer
Current Version: 10.0.0.0
Target Frameworks: .NET 6.0 (System.Data.SqlClient), .NET 8.0+ (Microsoft.Data.SqlClient)
Features
- Fluent SQL Builders – Type-safe query, insert, update, delete, and merge builders
- Full T-SQL Support – JOINs, CTEs, OUTPUT clauses, hints, TOP, DISTINCT, GROUP BY, and more
- ORM Capabilities – Attribute-based entity mapping with automatic serialization/deserialization
- Connection String Builder – Fluent API for building valid SQL Server connection strings
- Raw SQL Execution – Direct query execution with
SqlExecutefor complete control - Foreign Key Support – Automatic JOIN generation for related entities
- Soft Delete Support – Built-in soft-delete pattern implementation
- DDL Operations – Create tables directly from entity definitions
NOTE: The
SqlMergeBuilderis a complicated builder and is documented in its own readme file here: SqlMergeBuilder
Quick Start
1. Decorate Your Entity
using SujaySarma.Data.SqlServer.Attributes;
using SujaySarma.Data.Core.Attributes;
[SqlTable("Users", Schema = "dbo")]
public class User
{
[SqlTablePrimaryKeyColumn("UserId")]
[OrmPopulatedGuidField]
public Guid UserId { get; set; }
[SqlTableColumn("Username")]
public string Username { get; set; }
[SqlTableColumn("Email")]
public string Email { get; set; }
[SqlTableColumn("CreatedDate")]
[OrmPopulatedTimestampField]
public DateTime CreatedDate { get; set; }
[SqlTablePopulatedColumn("LastLogin")]
public DateTime? LastLogin { get; set; }
}
2. Build a Connection String
using SujaySarma.Data.SqlServer;
string connectionString = SqlConnectionStringBuilder
.UsingServerAddress("localhost")
.UsingDatabase("MyDatabase")
.UsingCredential("username", "password")
.EnableMultipleActiveResults()
.EnableEncryption()
.Build();
3. Query with SqlContext (ORM)
using SujaySarma.Data.SqlServer;
using (SqlContext context = SqlContext.Using(connectionString))
{
// Query users
var users = context.Select<User>()
.Where<User>(u => u.CreatedDate > DateTime.Now.AddDays(-30))
.OrderByDESC<User>(u => u.CreatedDate)
.Execute();
// Insert new user
User newUser = new User
{
Username = "john.doe",
Email = "john@example.com"
};
context.Insert(newUser);
// Update existing user
User existingUser = users.First();
existingUser.Email = "newemail@example.com";
context.Update(existingUser);
// Delete user
context.Delete(existingUser);
}
4. Build Queries with Fluent Builders
using SujaySarma.Data.SqlServer;
// Execute query
Result queryResult = SqlExecute.Query(connectionString, "SELECT * FROM Users WHERE Active = 1");
if (queryResult is QueryResult qr)
{
DataSet data = qr.Data;
// Process data...
}
// Execute stored procedure
var inputParams = new Dictionary<string, object?>
{
{ "@UserId", userId },
{ "@Status", "Active" }
};
var outputParams = new Dictionary<string, object>
{
{ "@RecordCount", 0 }
};
Result procResult = SqlExecute.ProcedureOrFunction( connectionString, "sp_UpdateUserStatus", inputParams, outputParams );
if (procResult is ProcedureOrFunctionResult pfr)
{
int returnValue = pfr.ReturnValue;
int recordCount = (int)pfr.ReturnParameters["@RecordCount"];
}
// Execute non-query (INSERT/UPDATE/DELETE)
Result nonQueryResult = SqlExecute.NonQuery( connectionString, "DELETE FROM Users WHERE LastLogin < '2020-01-01'" );
if (nonQueryResult is NonQueryResult nqr)
{
int affectedRows = nqr.AffectedRowCount;
}
API Reference
Attributes
Container-Level Attributes
| Attribute | Purpose |
|---|---|
SqlTable |
Maps a class/struct/record to a SQL Server table with schema support |
Properties:
Schema- The parent schema (default:"dbo")
Member-Level Attributes
| Attribute | Purpose |
|---|---|
SqlTableColumn |
Maps a property/field to a table column |
SqlTablePrimaryKeyColumn |
Marks a column as the PRIMARY KEY |
SqlTablePopulatedColumn |
Marks a column as populated by SQL Server (IDENTITY, DEFAULT, triggers, etc.) |
SqlTableForeignKeyColumn |
Marks a column as a FOREIGN KEY with automatic JOIN support |
SqlTableForeignKeyColumn Constructors:
// String-based FK (for dynamic references)
SqlTableForeignKeyColumn( string columnName, string referencedTableName, string referencedColumnName, string referencedTableAlias )
// Type-safe FK (with automatic join generation)
SqlTableForeignKeyColumn( string columnName, Type referencedTableEntity, string referencedPropertyOrField )
Properties:
Direction- Gets the FK relationship direction (ChildToParentorParentToChild)
Core Classes
SqlContext
The primary ORM entry point for database operations.
Initialization:
- SqlContext Using(SqlConnectionStringBuilder builder)
- SqlContext Using(string connectionString)
Operations:
Select<TEntity>()- Query entitiesInsert<TEntity>(entity)- Insert entityUpdate<TEntity>(entity)- Update entityDelete<TEntity>(entity)- Delete entityExecute()- Execute the built query
SqlExecute
Static class for executing raw SQL synchronously.
Methods:
- Result Query(string connectionString, StringBuilder query)
- Result Query(string connectionString, SqlQueryBuilder query)
- Result QueryScalar(string connectionString, StringBuilder query)
- Result QueryScalar(string connectionString, SqlQueryBuilder query)
- Result QueryBinary(string connectionString, StringBuilder query, int length, int index = 0, long offset = 0L)
- Result ProcedureOrFunction( string connectionString, string procedureName, Dictionary<string, object?> inputParameters, Dictionary<string, object>? outputParameters )
- Result ProcedureOrFunction(string connectionString, SqlExecBuilder execBuilder)
- Result NonQuery(string connectionString, StringBuilder script)
- Result NonQuery(string connectionString, SqlStatementBuilder script)
Return Types:
QueryResult- ContainsDataSetwith query resultsQueryScalarResult- Contains single scalar valueQueryBinaryResult- Contains binary dataProcedureOrFunctionResult- Contains procedure results, return value, and output parametersNonQueryResult- Contains affected row countErrorResult- Contains exception information
Fluent Builders
Note that all builders support the following methods as applicable:
WHERE clauses:
Where<TEntity>(Expression<Func<TEntity, bool>> condition)Where<T1, T2>(Expression<Func<T1, T2, bool>> condition)AndWhere<TEntity>(Expression<Func<TEntity, bool>> condition)OrWhere<TEntity>(Expression<Func<TEntity, bool>> condition)
JOINs
InnerJoin<T1, T2>(Expression<Func<T1, T2, bool>> condition, SqlHint hints)LeftJoin<T1, T2>(Expression<Func<T1, T2, bool>> condition, SqlHint hints)RightJoin<T1, T2>(Expression<Func<T1, T2, bool>> condition, SqlHint hints)FullJoin<T1, T2>(Expression<Func<T1, T2, bool>> condition, SqlHint hints)CrossJoin<T1, T2>(SqlHint hints)
Except SqlQueryBuilder, the other builders support output of affected rows via methods:
| Method | SqlInsertBuilder | SqlUpdateBuilder | SqlDeleteBuilder | SqlMergeBuilder |
|---|---|---|---|---|
| OutputInserted() | ✓ | ✓ | - | ✓ |
| OutputInserted(IEnumerable<string> columnNames) | ✓ | ✓ | - | ✓ |
| OutputDeleted() | - | ✓ | ✓ | ✓ |
| OutputDeleted(IEnumerable<string> columnNames) | - | ✓ | ✓ | ✓ |
| OutputToTable<TEntity>() | ✓ | ✓ | ✓ | ✓ |
| OutputToTable(string tableName) | ✓ | ✓ | ✓ | ✓ |
OutputInsertedcauses the INSERTED table to be written,OutputDeletedcauses the DELETED table to be written. WhenOutputToTableis specified, the rows are written to the specified table, otherwise they are returned as part of the resultset from the operation.
// INSERT with OUTPUT
SqlInsertBuilder insert = SqlInsertBuilder.Into<User>()
.Value<User>(u => u.Username, "newuser")
.OutputInserted(new[] { "UserId", "CreatedDate" })
.OutputToTable("@InsertedUsers");
// UPDATE with OUTPUT
SqlUpdateBuilder update = SqlUpdateBuilder.Into<User>()
.Set(user)
.OutputUpdated() // INSERTED.* and DELETED.*
.Where<User>(u => u.UserId == userId);
// DELETE with OUTPUT
SqlDeleteBuilder delete = SqlDeleteBuilder.From<User>()
.OutputDeleted(new[] { "UserId", "Username" })
.Where<User>(u => u.LastLogin < DateTime.Now.AddYears(-1));
Except SqlMergeBuilder, the other builders also support hints via method:
<builder>.With(SqlHint hints)
For SqlMergeBuilder, hints are specified in the initialiser:
SqlMergeBuilder.Using<TTable>.Create(SqlHint lockingHint = SqlHint.HoldLock, uint? top = null, bool topIsPercent = false);
-------------------------------------^^^^^^^^^^^^^^^^^^^^
SqlQueryBuilder
Builds SELECT statements with full T-SQL support.
Initialization:
SqlQueryBuilder queryBuilder = SqlQueryBuilder.From<TTable>();
Clauses:
- Select<TEntity>(Expression<Func<TEntity, object>>? columnSelector = null)
- Top(uint count, bool isPercent = false)
- Distinct() SqlQueryBuilder Into<TEntity>()
- Into(string tableName)
ORDER BY
- OrderByASC<TEntity>(Expression<Func<TEntity, object>> columns)
- OrderByDESC<TEntity>(Expression<Func<TEntity, object>> columns)
GROUP BY
- GroupByRollup<TEntity>(Expression<Func<TEntity, object>>? columns)
- GroupByCube<TEntity>(Expression<Func<TEntity, object>>? columns)
- GroupByGroupingSets<TEntity>(Expression<Func<TEntity, object>>? columns)
- GroupByEmpty<TEntity>()
Overriding SOFT-DELETE to query for soft-deleted rows*
- IncludingDeletedRows()
queryBuilder.Build();
SqlInsertBuilder
Builds INSERT statements.
Initialization:
SqlInsertBuilder insertBuilder = SqlInsertBuilder.Into<TTable>();
Methods:
- Top(uint count, bool isPercent = false)
- Value<TEntity>(Expression<Func<TEntity, object>> column, object value)
- UsingDefaultValues()
- From(SqlQueryBuilder query)
- From(StringBuilder query, IEnumerable<string>? columnNames = null)
- With(SqlHint hints)
insertBuilder.Build();
SqlUpdateBuilder
Builds UPDATE statements.
Initialization:
SqlUpdateBuilder updateBuilder = SqlUpdateBuilder.Into<TTable>();
METHODS
Update<TEntity>(TEntity entity)
UpdateMany<TEntity>(params IEnumerable<TEntity> entities) // Auto-generates WHERE on PK
Top(uint count, bool isPercent = false)
Set<TEntity>( TEntity entity, Dictionary<string, Expression<Func<TEntity, object>>>? additionalValues = null )
UpdateFrom<T1, T2>( Expression<Func<T1, T2, bool>> joinCondition, SqlHint hints, Dictionary<string, Expression<Func<T2, object>>> columnMappings )
updateBuilder.Build();
SqlDeleteBuilder
Builds DELETE statements.
Initialization:
SqlDeleteBuilder deleteBuilder = SqlDeleteBuilder.From<TTable>();
METHODS
Delete<TEntity>(TEntity entity)
DeleteMany<TEntity>(params IEnumerable<TEntity> entities) // Auto-generates WHERE on PK
Top(uint count, bool isPercent = false)
SqlConnectionStringBuilder
Fluent builder for SQL Server connection strings.
Initialization:
SqlConnectionStringBuilder builder = SqlConnectionStringBuilder.UsingServerAddress(string serverNameOrAddress);
Methods
- UsingDatabase(string database)
- UsingCredential(string userName, string password)
- UsingNamedInstance(string instanceName)
- UsingNamedPipes()
- UsingPort(ushort port)
Various flags
Flags that are not explicitly enabled using the below functions are left untouched, causing the connection to automatically use whatever is the default setting for that flag.
- EnableAllFlags()
- DisableAllFlags()
- EnablePersistSecurityInfo()
- DisablePersistSecurityInfo()
- EnableConnectionPooling()
- DisableConnectionPooling()
- EnableMultipleActiveResults()
- DisableMultipleActiveResults()
- EnableEncryption()
- DisableEncryption()
- EnableTrustServerCertificate()
- DisableTrustServerCertificate()
builder.Build();
Advanced Features
Foreign Key Relationships with Auto-JOIN
Consider the object definitions:
[SqlTable("Orders", Schema = "dbo")]
public class Order
{
[SqlTablePrimaryKeyColumn("OrderId")]
public int OrderId { get; set; }
[SqlTableForeignKeyColumn("CustomerId", typeof(Customer), nameof(Customer.CustomerId))]
public int CustomerId { get; set; }
[SqlTableColumn("OrderDate")]
public DateTime OrderDate { get; set; }
}
[SqlTable("Customers", Schema = "dbo")]
public class Customer
{
[SqlTablePrimaryKeyColumn("CustomerId")]
public int CustomerId { get; set; }
[SqlTableColumn("CustomerName")]
public string CustomerName { get; set; }
}
When querying Order, the ORM can automatically generate JOINs to Customer based on the foreign key relationship:
// Automatically generates JOIN
var query = SqlQueryBuilder.From<Order>()
.Select<Order>()
.InnerJoin<Order, Customer>((o, c) => o.CustomerId == c.CustomerId, SqlHint.None)
.Where<Order>(o => o.OrderDate > DateTime.Now.AddDays(-30));
Soft-delete support
"Soft-delete" is the mechanism of marking a row as deleted in the table without actually deleting it. The most common approach is to include a table-only column (typically a boolean type named IsDeleted) and marking this (as TRUE). While these rows exist on the table, they are never retrieved by any query or operation.
This library supports soft-delete natively via the SqlTableWithSoftDelete attribute at the container level:
using SujaySarma.Data.SqlServer.Attributes;
[SqlTableWithSoftDelete("Users", DeletedFieldName = "IsDeleted", Schema = "dbo")]
public class User
{
[SqlTablePrimaryKeyColumn("UserId")]
public Guid UserId { get; set; }
[SqlTableColumn("Username")]
public string Username { get; set; }
// NOTE that the IsDeleted column is NOT a part of the entity definition!
}
// Automatically excludes soft-deleted records
var activeUsers = SqlQueryBuilder.From<User>()
.Select<User>()
.Build();
/* Generates: "SELECT * FROM [dbo].[Users] WHERE [IsDeleted] = 0" */
// Include deleted records
var allUsers = SqlQueryBuilder.From<User>()
.Select<User>()
.IncludingDeletedRows()
.Build();
/* Generates: "SELECT * FROM [dbo].[Users] */
Complex queries with multiple JOINs
var query = SqlQueryBuilder.From<Order>()
.Select<Order>(o => new { o.OrderId, o.OrderDate })
.InnerJoin<Order, Customer>((o, c) => o.CustomerId == c.CustomerId, SqlHint.None)
.LeftJoin<Order, OrderDetail>((o, od) => o.OrderId == od.OrderId, SqlHint.None)
.Where<Order>(o => o.OrderDate >= DateTime.Now.AddMonths(-3))
.AndWhere<Customer>(c => c.Country == "USA")
.OrderByDESC<Order>(o => o.OrderDate)
.Top(100);
.Build();
NOTE: The
SqlMergeBuilderis a complicated builder and is documented in its own readme file here: SqlMergeBuilder
Requirements
- .NET 6.0 or higher
- System.Data.SqlClient (.NET 6.0) or Microsoft.Data.SqlClient (.NET 8.0+)
- SujaySarma.Data.Core 10.0.0.0 or higher
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request with clear descriptions
For issues, feature requests, or feedback, please create an issue on GitHub.
License
This library is licensed under the MIT License. Copyright (c) 2025 and beyond, Sujay V. Sarma. All rights reserved.
Author
Sujay V. Sarma
- GitHub: @sujayvsarma
- Repository: SujaySarma.Data
Important Notes
⚠️ Internal Members: This library contains public members intended only for use by
SujaySarma.Data.*implementation libraries. These are part of the internal implementation and should not be used directly by consumers. They are subject to change without notice.
🎯 T-SQL Parity: This library aims to provide feature parity with T-SQL. If something is possible in T-SQL, it should be possible through this library's API.
🚀 Version 10.0.0.0: Complete rewrite – NOT backwards compatible with previous versions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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
- Microsoft.Data.SqlClient (>= 6.1.3)
- SujaySarma.Data.Core (>= 10.0.0)
-
net6.0
- SujaySarma.Data.Core (>= 10.0.0)
- System.Data.SqlClient (>= 4.9.0)
-
net8.0
- Microsoft.Data.SqlClient (>= 6.1.3)
- SujaySarma.Data.Core (>= 10.0.0)
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 |
|---|---|---|
| 10.0.0 | 88 | 1/5/2026 |
| 9.5.15 | 180 | 10/16/2025 |
| 9.5.14 | 195 | 10/1/2025 |
| 9.5.12 | 182 | 10/1/2025 |
| 9.5.10 | 187 | 10/1/2025 |
| 9.5.8 | 187 | 10/1/2025 |
| 9.5.6 | 195 | 9/23/2025 |
| 9.5.5 | 319 | 9/18/2025 |
| 9.5.1 | 321 | 9/17/2025 |
| 9.5.0 | 180 | 6/20/2025 |
| 9.1.0 | 214 | 6/26/2024 |
| 8.9.7 | 191 | 2/15/2024 |
| 8.9.0 | 240 | 12/2/2023 |
| 8.8.5 | 207 | 11/30/2023 |
| 8.8.0 | 324 | 7/28/2023 |
| 8.7.5 | 275 | 6/27/2023 |
| 8.7.0 | 291 | 5/19/2023 |
| 8.2.0 | 344 | 3/28/2023 |
| 8.0.0 | 394 | 2/20/2023 |
[10.0.0.0] - Complete rewrite of the library! This version is NOT backwards compatible with previous versions.