EntitySpaces.ORM.SqlServer.NET
2026.11.0
dotnet add package EntitySpaces.ORM.SqlServer.NET --version 2026.11.0
NuGet\Install-Package EntitySpaces.ORM.SqlServer.NET -Version 2026.11.0
<PackageReference Include="EntitySpaces.ORM.SqlServer.NET" Version="2026.11.0" />
<PackageVersion Include="EntitySpaces.ORM.SqlServer.NET" Version="2026.11.0" />
<PackageReference Include="EntitySpaces.ORM.SqlServer.NET" />
paket add EntitySpaces.ORM.SqlServer.NET --version 2026.11.0
#r "nuget: EntitySpaces.ORM.SqlServer.NET, 2026.11.0"
#:package EntitySpaces.ORM.SqlServer.NET@2026.11.0
#addin nuget:?package=EntitySpaces.ORM.SqlServer.NET&version=2026.11.0
#tool nuget:?package=EntitySpaces.ORM.SqlServer.NET&version=2026.11.0
<img src="https://es-banner.paul-netstep.workers.dev?src=nuget_readme" alt="EntitySpaces" width="250" height="126">
EntitySpaces.ORM.SqlServer.NET
Part of the modernized EntitySpaces ORM — SQL Server data provider. Actively maintained fork with full .NET Framework 4.8 to .NET 10 support.
Supportability
| .NET targets | .NET Framework 4.8 · .NET 8 · .NET 9 · .NET 10 |
| SQL Server | SQL Server 2016 – 2025 (17.x) |
| Driver | Microsoft.Data.SqlClient 6.x / 7.x |
| Tested against | SQL Server 2016 Express (13.00.5026) · SQL Server 2022 · SQL Server 2025 Express (17.0.4085.5 / CU8+GDR) |
🛠️ Requires EntitySpaces Studio
This package provides the SQL Server runtime only — it does not generate code. Entity and Collection classes are generated from your database schema using EntitySpaces Studio, a separate WinForms tool.
- Download the latest MSI from Releases and run it. Requires the .NET 10 Desktop Runtime (x64).
- Older builds stay in EntitySpaces.Studio, tagged
-deprecated, for reference only.
Version alignment. Studio 2026.11 and the NuGet packages 2026.11.0 ship as one unit: the templates depend on provider changes from the same release. Do not mix a newer Studio with older packages, or the reverse. The previous Studio build (2026.08) remains available for projects still on packages 2026.8.0.
- Connect to your database under Settings → Connection, then run the Generated and Custom class templates.
New Features
- Concurrency exception detection covering the full set of SQL Server runtime errors (timestamp/rowversion mismatch, CHECK/FK violation, deadlock, lock timeout, unique/PK violation), all translated to
esConcurrencyException - Connection pool safety —
hasErrorflag drives best-effort cleanup and the connection is closed cleanly before returning to the pool. No explicitROLLBACKis issued from the provider — see the Transaction Semantics section and the Fixes entry about the defensive ROLLBACK removal. - Unified
OUTPUT INSERTED...INTO @output_valspattern — retrieves Identity, Computed, Concurrency (rowversion/timestamp), Defaults (GETDATE(), etc.),newsequentialid(), and special columns in a single round-trip. SET XACT_ABORT ONon all generated INSERT/UPDATE/DELETE statements, ensuring consistent transaction abort behavior on any runtime error- Server version auto-detection, cached per connection string —
conn.DatabaseVersionis now optional - Studio metadata engine now extracts
sys.columns.is_computed, precision/scale/max_length, androwversion/timestampconcurrency detection - Metadata-aware column/property name resolution in dynamic INSERT/UPDATE/DELETE generation — the provider now accepts both the canonical DB column name (
invoiceid) and the EntitySpaces property name (Invoiceid) when matching modified columns and reading values. On SQL Server both names usually coincide in the current schema, but the new form is robust to case-sensitive collations and to any future schema divergence. Aligned with the pattern in the PostgreSQL and MySQL providers. - Hierarchical save now propagates a user-supplied parent PK to child FKs across all three construction patterns:
parent.Collection.Add(child),parent.Collection.AddNew(), and directCollection.Save()calls. Previously, only auto-generated parent PKs (e.g.Orders.OrderIDwithIDENTITY) propagated reliably; parents with user-supplied keys (e.g.Customers.CustomerIDof typeNCHAR(5)) required an explicit FK assignment on each child. Aligned cross-provider. OUTPUT INSERTEDis now emitted for theDEFAULT VALUESbranch ofBuildDynamicInsertCommand. When an entity is empty (every column is nullable or has a DDL default) but the table has server-generated columns (IDENTITY, HasDefault, Computed, Concurrency), the generated INSERT now carriesOUTPUT INSERTED...INTO @output_vals DEFAULT VALUESso the IDENTITY id and any default-based read-back columns populate the entity afterSave().
Fixes
Corrected
IsComputedmetadata — no longer mis-identifiestimestampcolumns as computedError 547 (CHECK/FK violation) now reads the constraint name from
SqlError.Messageinstead ofSqlException.Message, ensuring it's available on every SQL Server version including 2025Worked around a SQL Server 2025 regression where
OUTPUT...INTO @table_variableinside a parameterized batch could silently suppress CHECK constraint exceptions — columns withHasDefault(non-GUID) now use a post-insertSELECT ... WHERE pk = SCOPE_IDENTITY()insteadHierarchical save now propagates the parent PK to the child FK even when the column and property names differ only in casing. Previously,
ApplyPostSaveKeyswrote the value under the property name (Invoiceid) while the provider searched for the column name (invoiceid), causing the FK to be inserted as NULL and the save to fail withSqlException 515. Verified againstInvoice→Invoicedetailhierarchies on SQL Server 2016 and SQL Server 2025 Express.In-memory entity state after a hierarchical save now reflects the propagated FK.
SyncCurrentValueKeysmerges any property-name-keyed entries into the canonical column-name slot insideAcceptChanges, so the typed getter (child.Invoiceid) returns the correct value without requiring a reload.GetExtraColumnswas updated to accept property names as valid identifiers for real columns, preventing the merged keys from being misclassified as extra columns in serialization payloads.Removed the defensive
IF @@TRANCOUNT > 0 ROLLBACKfrom thefinallyblocks of the SQL Server provider. On currentMicrosoft.Data.SqlClient, the command without an explicitTransactionassignment executes and aborts the transaction held by an ambientesTransactionScope, leaving itsTransaction.Connectionnull. SubsequentEnlistcalls then failed withInvalidOperationException: ExecuteNonQuery: Connection property has not been initialized. Rollback is the responsibility ofesTransactionScope.Dispose, which handles it correctly whenComplete()is not invoked. Aligned with PostgreSQL and MySQL, where the same pattern was removed.esEntityCollection.Save()now applies the collection-levelfksdictionary to Added children before the PreSave loop. Covers the parent-initiated pattern where the parent is saved before the children are added to the collection: in that scenarioApplyPostSaveKeys()of the parent runs with an empty collection, and the FK was only carried to the SQL parameters (correct DB value) but never written to the child'sCurrentValues(null getter after save).esEntityCollection.BulkInsert()now applies the collection-levelfksdictionary to Added children before building the save packets. Previously the FK was omitted fromModifiedColumns, causingSqlBulkCopyto write NULL for the FK column.esEntityCollection.Combine()now writes the target collection'sfksinto each combined entity usingSetProperty(comparing values throughSetValue) instead ofSetColumn(..., false). The prior unconditionalMarkFieldAsModifiedflipped Unchanged entities to Modified even when the FK value already matched, causing spurious UPDATEs on subsequentSave()calls.Dynamic INSERT no longer emits
SET @X = <ServerSideText>;for special columns. The generated BO storesProviderMetadata["DateAdded.ServerSideText"](and its siblings) as the bareword column name ("DateAdded"), which the provider was concatenating directly into the batch. Without aFROMcontext SQL Server rejected the statement withMsg 207 Invalid column name 'DateAdded'. In the same method,DateAddedandDateModifiedwere being registered twice in@output_vals— once by theHasDefaultbranch, once by the special-columns block — producingThe variable name '@DateAdded' has already been declared. Fix: aHashSet<string>tracks columns already added to@output_vals, the special-columns block skips duplicates, and it emits only theOUTPUT INSERTEDmachinery. The DBDEFAULTfills the column and the value is read back through@output_valsin a single round-trip. Verified againstInvoice→Invoicedetailon SQL Server 2022 and 2025 Express.Dynamic UPDATE resolves special-column
ServerSideTextbarewords to valid T-SQL expressions.BuildDynamicUpdateCommandwas emittingSET @pDateModified = DateModified;andSET @pModifiedBy = ModifiedBy;from the sameProviderMetadatasource, rejected withMsg 207 Invalid column name. A newResolveServerSideExpressionhelper translates the bareword in three steps: (1) if the value already contains(,',@, or aCURRENT_*prefix, use it as-is; (2) otherwise use the column's metadataDefault, stripping the outer parentheses SQL Server adds ((sysdatetime())→sysdatetime()); (3) otherwise fall back by well-known column name (DateAdded/DateModified→SYSDATETIME(),AddedBy/ModifiedBy→SYSTEM_USER). If nothing applies, theSETis omitted. Verified against the same hierarchy and SQL Server versions as the INSERT fix.Test suite robustness:
SP_Insert_Populates_DateAdded_From_Default_Sqlnow reads its reference timestamp from the SQL Server instance (SELECT SYSDATETIME()) instead ofDateTime.Now. The prior form assumed client and server shared a clock, which holds on a single-machine setup but breaks on any distributed deployment where the two clocks differ in timezone or skew — observed against SQL Server 2022 while the identical test passed against SQL Server 2025 Express on the same client. The assertion is now timezone-agnostic and the tolerance tightened from 5 s to 1 s because both timestamps come from the same clock. A separate design note is kept in the test suite: the schema uses timezone-dependentSYSDATETIME(); a future cycle may switch toSYSDATETIMEOFFSET()for distributed scenarios.Empty
Addedentities are now INSERTed instead of silently dropped.SaveDynamicEntitypreviously returned early wheneverCurrentValueswas null or empty, regardless ofRowState. That madenew EmptyEntity().Save()a no-op — the SQL never reached the server, no exception was raised, and the entity stayed withId = nullandRowState = Unchangedafter the subsequentAcceptChanges(). The early return is now conditional onRowState != Added, which enables two scenarios: (1) entities where all columns are nullable/default — the INSERT succeeds and IDENTITY is captured viaOUTPUT; (2) entities with NOT NULL columns that have no default — the INSERT is rejected withSqlException 515, which must propagate to the caller. MySQL does not have this early return; the fix aligns SQL Server behavior with MySQL.Metadata-aware column/property lookup is now applied to all three
StoredProcedurecode paths. Previously,PopulateStoredProcParameters,BuildStoredProcDeleteCommand, andSaveStoredProcCollection's preprocess-parameters block usedContainsKey(col.Name)/ direct dictionary indexing. WhenApplyPostSaveKeyswrote a child FK under the property name (Invoiceid) but the SP parameter was named after the column (invoiceid), the value was silently bound asNULLand the INSERT failed withSqlException 515. The same divergence was already tolerated on the Dynamic path (BuildDynamicInsertCommand/Update/Delete), but was latent on the SP path because no prior test exercised a hierarchical save through stored procedures. All three SP paths now route through the sharedGetColumnValue/GetOriginalColumnValuehelpers.
Known Limitation — SQL Server 2025 Express
CHECK/FK constraint violations inside parameterized batches are silently swallowed by the server — the row is persisted with invalid values and no
SqlExceptionreaches the provider (confirmed on Express 17.0.4085.5 / CU8+GDR, September 2026). Independent of constraint type (CHECK vs FK), ambient transaction, and presence ofOUTPUT INSERTED. Does not affect SQL Server 2016 Express (13.00.5026) or SQL Server 2022. Track upstream at dotnet/SqlClient.
Transaction Semantics
XACT_ABORT ON — the scope is single-use after a constraint violation
SET XACT_ABORT ON is applied to every generated INSERT/UPDATE/DELETE. When a statement fails (CHECK, FK, NOT NULL, arithmetic overflow), SQL Server rolls back the entire transaction server-side. The esTransactionScope object still exists on the client side but its enlisted connection is now unusable.
The next command in the same scope fails with:
InvalidOperationException: ExecuteNonQuery: Connection property has not been initialized
This is expected. To recover, dispose the dead scope and open a new one:
using (var scope1 = new esTransactionScope())
{
try { /* ... */ scope1.Complete(); }
catch (SqlException) { /* scope is dead, do NOT Complete() */ }
}
using (var scope2 = new esTransactionScope())
{
/* ... fresh transaction, works normally ... */
scope2.Complete();
}
This behavior is verified by TransactionScopeRegressionTests.TransactionScope_Is_Aborted_By_XACT_ABORT_After_Check_Violation and New_Scope_After_Check_Violation_Works. The former is skipped (Inconclusive) on SQL Server 2025 Express because the underlying CHECK violation is silently swallowed by the server — see the Known Limitation above.
Dependency Updates
- Updated
System.Configuration.ConfigurationManagerfrom10.0.13to10.0.14- Maintenance release. No API changes affecting EntitySpaces.
Quick Samples
Load a collection:
var customers = new CustomersCollection();
customers.LoadAll();
foreach (var customer in customers)
{
Console.WriteLine(customer.CompanyName);
}
Create, update, delete an entity:
var employee = new Employees();
employee.FirstName = "Joe";
employee.LastName = "Smith";
employee.Save(); // Create
employee.LastName = "Doe";
employee.Save(); // Update
employee.MarkAsDeleted();
employee.Save(); // Delete
SQL Server connection string:
esProviderFactory.Factory = new EntitySpaces.Loader.esDataProviderFactory();
esConnectionElement conn = new esConnectionElement();
conn.Provider = "EntitySpaces.SqlClientProvider";
conn.ConnectionString = "User ID=sa;Password=mypassword;Initial Catalog=Northwind;" +
"Data Source=localhost;TrustServerCertificate=True;";
esConfigSettings.ConnectionInfo.Connections.Add(conn);
More usage examples (joins, paging, transactions, and the full Fluent SQL API): see the main EntitySpaces README.
Generating your entity classes
See Requires EntitySpaces Studio above.
If this provider saved you time, you can support its development via PayPal. Feedback is welcome through the 3‑minute roadmap survey.
| Product | Versions 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 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. |
| .NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- Microsoft.Data.SqlClient (>= 7.1.1)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
-
net10.0
- Microsoft.Data.SqlClient (>= 7.1.1)
- System.Configuration.ConfigurationManager (>= 10.0.14)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
-
net8.0
- Microsoft.Data.SqlClient (>= 7.1.1)
- System.Configuration.ConfigurationManager (>= 10.0.14)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
-
net9.0
- Microsoft.Data.SqlClient (>= 7.1.1)
- System.Configuration.ConfigurationManager (>= 10.0.14)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Actively maintained fork of the EntitySpaces ORM, modernized for .NET Framework 4.8 through .NET 10 and aligned with EntitySpaces Studio 2026.11.
SQL Server: OUTPUT INSERTED for all server-generated columns in a single round-trip, SET XACT_ABORT ON on generated statements, full concurrency exception translation (532, 547, 1205, 1222, 2601, 2627) and Microsoft.Data.SqlClient for SQL Server 2016-2025.
Full release notes: https://github.com/paulcordova/EntitySpaces/blob/master/CHANGELOG.md