NSLabs.EFCore.Extensions.Sqlite 0.9.0

dotnet add package NSLabs.EFCore.Extensions.Sqlite --version 0.9.0
                    
NuGet\Install-Package NSLabs.EFCore.Extensions.Sqlite -Version 0.9.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="NSLabs.EFCore.Extensions.Sqlite" Version="0.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NSLabs.EFCore.Extensions.Sqlite" Version="0.9.0" />
                    
Directory.Packages.props
<PackageReference Include="NSLabs.EFCore.Extensions.Sqlite" />
                    
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 NSLabs.EFCore.Extensions.Sqlite --version 0.9.0
                    
#r "nuget: NSLabs.EFCore.Extensions.Sqlite, 0.9.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 NSLabs.EFCore.Extensions.Sqlite@0.9.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=NSLabs.EFCore.Extensions.Sqlite&version=0.9.0
                    
Install as a Cake Addin
#tool nuget:?package=NSLabs.EFCore.Extensions.Sqlite&version=0.9.0
                    
Install as a Cake Tool

NSLabs.EFCore.Extensions

Batched conditional bulk update / upsert for Entity Framework Core — execute N different WHERE + SET operations in one round-trip with sequential semantics (later ops see earlier writes) and caller-controlled transactions.

Why

Standard EF Core:

  • ExecuteUpdateAsync → 1 filter + 1 payload per call = N round-trips
  • BulkExtensions / FlexLabs.Upsert → PK-only or 1 op per call

This library: N heterogeneous UPDATE / UPSERT / DELETE across multiple tables, one DbCommand, param-budget chunking (~2100 params on SQL Server), per-op RowsAffected.

Installation

Requirements: .NET 10 and Microsoft.EntityFrameworkCore 10.0.0

Choose your database provider:

SQL Server

dotnet add package NSLabs.EFCore.Extensions
dotnet add package NSLabs.EFCore.Extensions.SqlServer

SQLite

dotnet add package NSLabs.EFCore.Extensions
dotnet add package NSLabs.EFCore.Extensions.Sqlite

PostgreSQL

dotnet add package NSLabs.EFCore.Extensions
dotnet add package NSLabs.EFCore.Extensions.Npgsql

MySQL

Coming soon. Provider support is being expanded.

Quick Start

Bulk Batch (multi-table, single round-trip)

var today = DateTime.UtcNow.Date;
var result = await db.BulkExecuteAsync(b =>
{
    b.Update<Item>(op => op.Where(x => x.Id == 6)
                           .Set(x => x.Key1, "Value1")
                           .Set(x => x.Key2, 5));

    b.Update<Order>(op => op.Where(x => x.Status == OrderStatus.Pending)
                            .Set(x => x.Status, OrderStatus.Shipped));

    // Atomic page-view counter: row exists -> Views + 1 inside the UPDATE;
    // row missing -> insert with Views = 1. No read-modify-write round-trip.
    b.Upsert<DailyArticleViews>(u => u.MatchOn(v => new { v.ArticleId, v.Date })
                                      .Update(v => v.Views, v => v.Views + 1)
                                      .Insert(new DailyArticleViews { ArticleId = 42, Date = today, Views = 1 }));
});

// per-op counts (SQL Server)
result.Operations[0].RowsAffected;

Simple Helper (single table)

If all your updates are for the same table, you can use this shorter way:

await db.Items.BulkUpdateAsync(b =>
{
    b.Add(op => op.Where(x => x.Id == 6).Set(x => x.Key1, "Value1"));
    b.Add(op => op.Where(x => x.Key1 == "Old").Set(x => x.Key3, 0));
});

Deferred Builder

IBulkBatch batch = db.CreateBulkBatch();
batch.Update<Item>(op => op.Where(x => x.Id == 6).Set(x => x.Key1, "Value1"));
ApplyRules(batch);
var result = await batch.ExecuteAsync(ct);

Transactions

Bulk operations do not create a transaction by default (matches EFCore.BulkExtensions and EF Core ExecuteUpdate behavior). For atomic all-or-nothing execution across multiple operations or mixed SaveChanges, start a transaction yourself:

// 1. No transaction (default) — each statement commits individually
var reading = new EnergyReading
{
    MeterId = "MTR-1001",
    Date = DateTime.UtcNow.Date,
    ConsumptionKwh = 18.42,
    RecordedAt = DateTime.UtcNow
};

var r = await db.BulkExecuteAsync(b =>
{
    b.Update<Item>(op => op.Where(x => x.Id == 6).Set(x => x.Key1, "V1"));
    b.Upsert<EnergyReading>(u => u.MatchOn(m => new { m.MeterId, m.Date })
                                  .Update(m => m.ConsumptionKwh, reading.ConsumptionKwh)
                                  .Update(m => m.RecordedAt, reading.RecordedAt)
                                  .Insert(reading));
});

// 2. Caller-managed transaction — atomic across all operations
await using var tx = await db.Database.BeginTransactionAsync();
try
{
    await db.BulkExecuteAsync(b => { b.Update<Item>(...); b.Delete<AuditLog>(...); });
    await db.SaveChangesAsync(); // optional — participates in same transaction
    await tx.CommitAsync();
}
catch { await tx.RollbackAsync(); throw; }

// 3. Integrate with existing ADO.NET transaction
await db.Database.UseTransactionAsync(connTx);
await db.BulkExecuteAsync(b => { ... });

The executor piggybacks on Database.CurrentTransaction and never commits/rollbacks itself. ThrowIfZeroAffected is validated after all chunks — without a transaction, prior chunks are already committed; with a transaction, the caller can roll back.

Documentation & Support

License

MIT

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
0.9.0 0 9/19/2026
0.8.0 81 9/14/2026
0.7.0 96 9/10/2026
0.6.0 99 9/5/2026
0.5.0 88 9/4/2026
0.4.0 94 9/4/2026