diff --git a/.zenodo.json b/.zenodo.json
index b99df0c..d6dd3d7 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -1,6 +1,6 @@
{
"title": "NetCoreApplicationTemplate",
- "version": "2.1.0",
+ "version": "2.2.0",
"upload_type": "software",
"description": "A reusable ASP.NET Core application template for secure, maintainable, production-oriented .NET applications. It organizes startup composition, middleware ordering, structured logging, forwarded headers, security headers, rate limiting, centralized error handling, authentication and authorization foundations, EF Core data access patterns, CI validation, template packaging, and DocFX documentation.",
"creators": [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e628d7..5e8b237 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,31 @@ All notable changes to this project are documented in this file.
This project follows Semantic Versioning using the format `MAJOR.MINOR.PATCH`.
+## 2.2.0 - 2026-06-29
+
+### Added
+
+* Added `IApplicationSaveChangesPipeline` and `ApplicationSaveChangesPipeline` to move EF Core save preparation out of `ApplicationDbContext` and into an application-owned persistence pipeline.
+* Added `ApplicationSaveChangesInterceptor` as the composite EF Core save lifecycle interceptor for invoking the save pipeline through `SavingChanges` / `SavingChangesAsync` and `SavedChanges` / `SavedChangesAsync` hooks.
+* Added EF Core save pipeline documentation covering default pipeline order, extension seams, audit lifecycle safety, and the decision to keep a composite interceptor by default.
+* Added ADR 0004 documenting the decision to keep the composite SaveChanges interceptor until a concrete consumer or maintenance need justifies specialized interceptors.
+* Added tests that verify sync and async save-pipeline invocation through `ApplicationDbContext`.
+* Added branch-focused tests for `ApplicationSaveChangesInterceptor`, including constructor null-guard, non-`ApplicationDbContext`, and bounded after-save follow-up branches.
+
+### Changed
+
+* Reduced repeated EF Core `ChangeTracker` inspection by materializing Added/Modified/Deleted entries once and reusing that list across string canonicalization, lookup normalization, timestamp normalization, concurrency stamping, and audit entry creation.
+* Reduced `ApplicationDbContext` save overrides to optimistic-concurrency exception handling around EF Core's native save flow.
+* Kept `ConcurrencyStamp` as the default application-managed optimistic concurrency token and documented why that remains the portable SQLite / SQL Server baseline.
+* Updated package icon and favicon image assets used for repository, NuGet package, and documentation branding.
+* Updated release metadata, package README examples, template packaging docs, citation metadata, and Zenodo metadata for `2.2.0`.
+
+### Notes
+
+* This is a minor release because it introduces and documents a clearer EF Core save-pipeline extension seam while preserving the stable `2.x` package identity, template short name, template options, and default scaffold behavior.
+* The default generated scaffold continues to use the same package identity, template identity, authentication options, data-access options, and local SQLite development path.
+* SQL Server-only consumers may still replace the application-managed concurrency token with provider-native rowversion behavior when appropriate, but the template default remains provider-portable.
+
## 2.1.0 - 2026-06-27
### Added
diff --git a/CITATION.cff b/CITATION.cff
index a3f733f..ca287a8 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -1,8 +1,8 @@
cff-version: 1.2.0
message: "If you use this software, please cite it using the metadata below."
title: "NetCoreApplicationTemplate"
-version: "2.1.0"
-date-released: "2026-06-27"
+version: "2.2.0"
+date-released: "2026-06-29"
repository-code: "https://github.com/cdcavell/NetCoreApplicationTemplate"
url: "https://cdcavell.github.io/NetCoreApplicationTemplate/"
type: software
diff --git a/Directory.Build.props b/Directory.Build.props
index 0da3fed..6db21f1 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,12 +5,12 @@
enable
latest
- 2.1.0
+ 2.2.0
$(VersionPrefix)
$(VersionPrefix)-$(VersionSuffix)
- 2.1.0.0
- 2.1.0.0
+ 2.2.0.0
+ 2.2.0.0
$(Version)+$(RepositoryUrl)
true
diff --git a/NetCoreApplicationTemplate.Template.csproj b/NetCoreApplicationTemplate.Template.csproj
index 4c78b74..8e61b23 100644
--- a/NetCoreApplicationTemplate.Template.csproj
+++ b/NetCoreApplicationTemplate.Template.csproj
@@ -13,8 +13,8 @@
LICENSE.txt
PACKAGE-README.md
PACKAGE-ICON.png
- Minor 2.1.0 release of the NetCoreApplicationTemplate dotnet new template. This release adds an optional template-owned audit storage seam, documents audit storage extension paths, improves no-op SaveChanges handling, expands production authentication and middleware guidance, adds optional application/domain layering guidance, and refreshes documentation/image assets while preserving the stable 2.x package identity and default scaffold behavior.
- 2.1.0
+ Minor 2.2.0 release of the NetCoreApplicationTemplate dotnet new template. This release refactors EF Core SaveChanges preparation into an application-owned save pipeline invoked through a composite SaveChangesInterceptor, reduces repeated ChangeTracker enumeration, documents the EF Core save pipeline extension model, clarifies application-managed concurrency stamp portability, and refreshes package/documentation image assets while preserving the stable 2.x package identity and default scaffold behavior.
+ 2.2.0
$(VersionPrefix)
Template
diff --git a/PACKAGE-README.md b/PACKAGE-README.md
index f22de4e..8f2db2b 100644
--- a/PACKAGE-README.md
+++ b/PACKAGE-README.md
@@ -20,13 +20,13 @@ This README is intended for NuGet package consumers. The full repository README
Install the template package from NuGet:
```text
-dotnet new install NetCoreApplicationTemplate::2.1.0
+dotnet new install NetCoreApplicationTemplate::2.2.0
```
For local package validation, install a packed package directly:
```text
-dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.1.0.nupkg
+dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.2.0.nupkg
```
## Generate a project
@@ -79,7 +79,7 @@ dotnet test --configuration Release
Install the newer package version with the same package identity:
```text
-dotnet new install NetCoreApplicationTemplate::2.1.0
+dotnet new install NetCoreApplicationTemplate::2.2.0
```
## Uninstall
diff --git a/README.md b/README.md
index 4e6ee89..7924cc6 100644
--- a/README.md
+++ b/README.md
@@ -19,9 +19,9 @@ This repository provides a working application baseline with common infrastructu
## Current Release
-Current release: __[Release 2.1.0](https://github.com/cdcavell/NetCoreApplicationTemplate/releases/tag/v2.1.0)__
+Current release: __[Release 2.2.0](https://github.com/cdcavell/NetCoreApplicationTemplate/releases/tag/v2.2.0)__
-Tag: `v2.1.0`
+Tag: `v2.2.0`
## Project Goals
@@ -163,7 +163,7 @@ The scaffolded consumer output intentionally excludes repository-maintainer cont
Install the published template package:
```powershell
-dotnet new install NetCoreApplicationTemplate::2.1.0
+dotnet new install NetCoreApplicationTemplate::2.2.0
```
### Pack and Install Locally
@@ -177,7 +177,7 @@ dotnet pack ./NetCoreApplicationTemplate.Template.csproj --configuration Release
Install the generated package:
```powershell
-dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.1.0.nupkg
+dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.2.0.nupkg
```
Generate a consumer project:
@@ -234,7 +234,7 @@ The non-default scaffold preserves the template's core guardrails, including str
Update the installed template by installing a newer package version:
```powershell
-dotnet new install NetCoreApplicationTemplate::2.1.0
+dotnet new install NetCoreApplicationTemplate::2.2.0
```
Uninstall the template package:
@@ -426,12 +426,12 @@ If you use this repository, please cite it using the metadata in [`CITATION.cff`
- Suggested plain-text citation:
```text
-Cavell, Christopher D. NetCoreApplicationTemplate. Version 2.1.0. Zenodo. MIT License. https://doi.org/10.5281/zenodo.20373042
+Cavell, Christopher D. NetCoreApplicationTemplate. Version 2.2.0. Zenodo. MIT License. https://doi.org/10.5281/zenodo.20373042
```
## Roadmap
-The project is a reusable .NET application template with a stable `2.1.0` package baseline. Future work may include additional provider modules, expanded examples, optional template parameters, and continued hardening of the documented release surface.
+The project is a reusable .NET application template with a stable `2.2.0` package baseline. Future work may include additional provider modules, expanded examples, optional template parameters, and continued hardening of the documented release surface.
See [Template Packaging](https://cdcavell.github.io/NetCoreApplicationTemplate/articles/template-packaging.html) for the current packaging direction.
diff --git a/docs/articles/template-packaging.md b/docs/articles/template-packaging.md
index 897fa62..cfbb207 100644
--- a/docs/articles/template-packaging.md
+++ b/docs/articles/template-packaging.md
@@ -16,7 +16,7 @@ Package-based validation is preferred because it verifies the actual distributio
| Template identity | `CDCavell.NetCoreApplicationTemplate.CSharp` |
| Template group identity | `CDCavell.NetCoreApplicationTemplate` |
| Source replacement token | `ProjectTemplate` |
-| Current package version | `2.1.0` |
+| Current package version | `2.2.0` |
The `2.0.0` release moved the public NuGet package ID to `NetCoreApplicationTemplate`. The internal template identity and group identity remain unchanged for template metadata continuity.
@@ -92,13 +92,13 @@ dotnet pack ./NetCoreApplicationTemplate.Template.csproj --configuration Release
Install the published package from NuGet:
```powershell
-dotnet new install NetCoreApplicationTemplate::2.1.0
+dotnet new install NetCoreApplicationTemplate::2.2.0
```
Install a locally packed package:
```powershell
-dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.1.0.nupkg
+dotnet new install ./artifacts/template-package/NetCoreApplicationTemplate.2.2.0.nupkg
```
## Create a New Project from the Template