EntitySpaces.ORM.PostgreSQL.NET
2026.11.0
dotnet add package EntitySpaces.ORM.PostgreSQL.NET --version 2026.11.0
NuGet\Install-Package EntitySpaces.ORM.PostgreSQL.NET -Version 2026.11.0
<PackageReference Include="EntitySpaces.ORM.PostgreSQL.NET" Version="2026.11.0" />
<PackageVersion Include="EntitySpaces.ORM.PostgreSQL.NET" Version="2026.11.0" />
<PackageReference Include="EntitySpaces.ORM.PostgreSQL.NET" />
paket add EntitySpaces.ORM.PostgreSQL.NET --version 2026.11.0
#r "nuget: EntitySpaces.ORM.PostgreSQL.NET, 2026.11.0"
#:package EntitySpaces.ORM.PostgreSQL.NET@2026.11.0
#addin nuget:?package=EntitySpaces.ORM.PostgreSQL.NET&version=2026.11.0
#tool nuget:?package=EntitySpaces.ORM.PostgreSQL.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.PostgreSQL.NET
Part of the modernized EntitySpaces ORM — PostgreSQL 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 |
| PostgreSQL | PostgreSQL 13 – 18 (validated with 17.11.0 and 18.6.0) · Neon PostgreSQL (serverless cloud) compatible |
| Driver | Npgsql 7.x – 10.x |
🛠️ Requires EntitySpaces Studio
This package provides the PostgreSQL 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
GENERATED BY DEFAULT AS IDENTITYandGENERATED ALWAYS AS IDENTITY— auto and explicit PK insert supportedINSERT ... RETURNINGconsolidated into a single round-trip for all output columns (identity PKs, defaults, concurrency and special columns)Tolerant column/property name resolution — the provider accepts both the database column name and the generated property name when deciding whether a column is part of an INSERT/UPDATE and when mapping server-returned values back to the entity. This covers:
snake_case→ PascalCase conversion:order_id↔OrderId,customer_id↔CustomerId.- Arbitrary divergence between a FK column and the PK column it references:
invoice.idreferenced byinvoicedetail.invoiceid— a column that does not follow the schema's naming pattern and does not split on an underscore. - PascalCase-only divergence:
invoiceidcolumn with anInvoiceidproperty.
No code changes required on your side; the tolerant resolution is applied uniformly to INSERT, UPDATE, DELETE, and server-returned value mapping.
Single-pass RETURNING with
CommandBehavior.SingleRow— the reader is closed cleanly after the first row, keeping the connection usable for the next command in the sameesTransactionScope. Eliminates intermittent25P02cascades in hierarchical batches.StoredProcedure path fully functional — the provider invokes PostgreSQL functions via
SELECT * FROM "proc_X"(:args)and reads the returned rowset. See StoredProcedure Path below.DELETEenforces optimistic concurrency — the generatedproc_<table>Deletenow includes the concurrency column in its signature andWHEREclause. A staleVersiontriggersRAISE EXCEPTION ... USING ERRCODE = 'P0001'; the provider'sCheckForConcurrencyExceptionmapsP0001(with theesConcurrencyExceptionmessage prefix) toesConcurrencyException. Same behaviour as MySQL, SQL Server, and Oracle.UPDATEusesRETURNINGinstead of a secondSELECT— the dynamic SQL UPDATE path now emitsUPDATE ... SET ... WHERE ... RETURNING ...as a single statement. Npgsql rejects multiple queries combined withOutput/InputOutputparameters ("Commands with multiple queries cannot have out parameters"), so the previous shape broke the dynamic update path on entities with server-side special columns. The Insert path already used RETURNING; this makes Update consistent.Concurrency exception detection and translation to
esConcurrencyException(unique/PK violation, serialization failure, deadlock, lock timeout)Automatic
APPLY→LATERALjoin translation — writeOuterApply/CrossApplyonce, get nativeLEFT JOIN LATERAL/JOIN LATERALon PostgreSQL, no code changes needed when switching providersServer version auto-detection, cached per connection string — supports multiple PostgreSQL instances of different versions simultaneously
Neon PostgreSQL (serverless cloud) connection support
Studio metadata engine now extracts
is_generated(computed columns),is_identity/pg_get_serial_sequence(auto-increment),numeric_precision/numeric_scale, andtimestampconcurrency detectionThread-safe parameter cache — the parameter cache is a
ConcurrentDictionarykeyed byDataID. No lock contention in multi-threaded environments; the cache is built once per entity type and reused safely across threads.
Fixes
StoredProcedure save path was non-functional — the provider used
CommandType.StoredProcedure, which Npgsql 7/8 translates toCALL, valid only for PROCEDURES. PostgreSQL rejects this with SQLSTATE 42883 when the target is a FUNCTION. Migrated toCommandType.TextwithSELECT * FROM "func"(:args)and reading the returned rowset. See StoredProcedure Path.Special columns (
DateModified,AddedBy,ModifiedBy) now return their own columns — previously all three incorrectly addedDateAddedto theRETURNINGclause, so the actual server-side values were never propagated back to the entity. This is a copy-paste bug in the INSERT builder that affected every generated entity using server-side auditing columns.Server-side special columns no longer sent as parameters on INSERT — the Studio-generated
ServerSideTextis the column name itself (e.g."DateAdded"), which is not a valid SQL expression. The provider now excludes these columns from the INSERT and lets the DBDEFAULTpopulate them, fetching the value back viaRETURNING.Explicit PK insert on IDENTITY columns — when a user assigns a value to an auto-increment PK, the column is now sent as
InputOutputand included inRETURNING, so the entity is marked clean after save and the explicit value is preserved.DEFAULTcolumns not explicitly assigned now correctly sendDBNull.Valueinstead ofnull— Npgsql raises a parameter exception onnull, this is now handled automatically.Explicit
ROLLBACKremoved from error paths — the provider no longer issuesROLLBACKon enlisted connections. On Npgsql 6+ /System.Transactionsthis could abort the ambient transaction, not just the current command. Transaction rollback is now delegated entirely toesTransactionScope, which is the correct owner. The previous defensive code was actively harmful.Type resolution corrected to use dual
udt_name+data_typemapping, avoiding mismatches on custom/aliased PostgreSQL typesDynamic UPDATE failed on entities with server-side special columns —
BuildDynamicUpdateCommandappended a second statement after a semicolon to read the server-generated columns back (UPDATE ...; SELECT "DateModified", "Version" FROM ...). Npgsql raisesNotSupportedException: Commands with multiple queries cannot have out parameterswhen the command carriesOutput/InputOutputparameters — which it does, forDateModified,ModifiedBy, andVersion. Replaced the; SELECT ...suffix withRETURNING .... The existingExecuteInsertCommand/MapOutputParameterspath already knew how to read columns back from a returned row, so no other change was required.DELETEon staleVersionwas silently dropping the row — the generatedproc_XDeletefunction accepted only PK columns, so theWHEREclause never matched onVersionand a stale in-memory copy could delete a row updated by another session. Fixed at three levels: the SP template includes the concurrency column in the function signature andWHEREclause, the provider'sBuildStoredProcDeleteCommandsends it as an argument, and the exception translator mapsP0001toesConcurrencyException. The same fix pattern was previously applied to MySQL.Dynamic INSERT on tables with user-settable
IsConcurrencycolumns — PostgreSQLIsConcurrencycolumns are nullable timestamps (invoicedate,paidat,duedate, ...), not server-generatedrowversionlike on SQL Server. The provider now (a) bindsDBNull.Valuewhen the user did not assign a value (Npgsql rejectsnullonInputOutputparameters withInvalidOperationException: Parameter ':X' cannot be null, DBNull.Value should be used instead), and (b) includes the column in the INSERTVALUESlist when the user did assign a value — previouslyNOT NULLconcurrency columns likebilling_invoice.invoicedatewere silently omitted and rejected by the server withSQLSTATE 23502.RETURNINGrefreshes the value intoCurrentValuesin both cases.MapOutputParametersno longer NREs on emptyCurrentValues— the previous implementation iteratedesSmartDictionary.Keysto match parameters to columns by normalized name.KeysthrowsNullReferenceExceptionwhen the internal dictionary has never been written to — the exact state of anAddedentity with no user-assigned properties (e.g. an emptybilling_defaults_probeinserted viaINSERT ... DEFAULT VALUES RETURNING ...).ContainsKeywas null-safe;Keysis not. Replaced theKeysiteration with metadata lookup (columns.FindByColumnName) and dual-slot writes (column name + property name), mirroring whatMapReaderRowToCurrentValuesalready does on the SP path. NoKeysaccess, no NRE.Dynamic UPDATE on tables with
IsConcurrencycolumns — three defects in the same branch ofBuildDynamicUpdateCommand:- Parameter value never set — same class of bug as the INSERT path. Npgsql rejects
InputOutputparameters withnullbefore the SQL reaches the server. - No
ANDseparator between concurrencyWHEREclauses —conncurwas concatenated with+=and no separator. One concurrency column produced valid SQL; two or more produced... @p4"col5" IS NOT DISTINCT FROM @p5 ...(syntax error).billing_invoicehas sixIsConcurrencycolumns. This bug was pre-existing in the provider, not introduced by the Billing port — it had never been surfaced because the Northwind metadata has exactly oneIsConcurrencycolumn. = @pwith@p = NULLnever matches in SQL. PostgreSQL timestamps on concurrency columns areNULLin the common case (duedate,lastreminderat,invoicefrommonth, ...), so the default path hit zero rows and threw a spuriousesConcurrencyExceptionon a single-user, same-session flow. Replaced withIS NOT DISTINCT FROM(NULL-safe equality, PG 8.2+).
The combined fix binds the original value with
DBNull.Valuefallback, setsDirection = Input(theWHEREvalue is the original — the DB does not modify it, nothing to read back), usesIS NOT DISTINCT FROM, and threads a localconncurAndseparator through the loop.SourceVersion = DataRowVersion.Originalwas removed (no-op on rawNpgsqlCommand, only effective viaNpgsqlDataAdapter).- Parameter value never set — same class of bug as the INSERT path. Npgsql rejects
IsEntitySpacesConcurrencyUPDATE branch fixed preemptively — latent, not exercised by any Billing table (billing_*usesIsConcurrencytimestamps, notIsEntitySpacesConcurrency). The same class of defect existed inBuildDynamicUpdateCommand:scommawas not updated after emitting theSETclause (a subsequent modified column concatenated without a comma), theWHEREclause was concatenated without theANDseparator (same bug as above), andp.Valuewas not guarded for null. Applied preemptively; a new BO usingIsEntitySpacesConcurrencyshould be validated against the same scenarios asBilling02(seeKnown Limitationsbelow).
⚠️ Known Limitations
Dynamic UPDATE does not persist changes to IsConcurrency columns
BuildDynamicUpdateCommand's first branch excludes IsConcurrency from the SET clause:
if (isModified && (!col.IsAutoIncrement && !col.IsConcurrency && !col.IsEntitySpacesConcurrency))
{
// add to SET
}
Consequence: if the application modifies invoicedate, duedate, or any other IsConcurrency column on a loaded entity, the change is silently discarded. If the only modification is an IsConcurrency column, the SET clause is empty and the generated SQL is syntactically invalid.
Not exercised by the Billing test suite (the tests modify invoicenumber, total, ispaid, paymentid — never a concurrency column). Deferred to the next cycle; the fix requires two parameters per concurrency column (one for SET with CurrentValues, one for WHERE with OriginalValues) plus corresponding handling in MapOutputParameters.
Workaround: assign the column's new value via a raw UPDATE if needed.
⚠️ Transaction Management
Use esTransactionScope for transactions — not System.Transactions.TransactionScope.
Starting with Npgsql 6.0, opening a connection while an ambient System.Transactions.TransactionScope is active no longer enlists that connection in the transaction automatically. If your application relies on TransactionScope for rollback, the INSERT/UPDATE will run in autocommit mode and the rollback will not revert your data.
The provider participates correctly in EntitySpaces' native esTransactionScope, which is the supported mechanism across all EntitySpaces providers:
using (var scope = new esTransactionScope())
{
var employee = new Employees { FirstName = "Mike", LastName = "Griffin" };
employee.Save();
var product = new Products { ProductName = "Some Gadget" };
product.Save();
scope.Complete(); // commit; omit to roll back
}
Nested esTransactionScope instances are supported — the inner scope votes on the outer transaction. Omitting Complete() on any scope that owns the root transaction rolls back everything created inside it.
If you also need System.Transactions interop, keep in mind that EntitySpaces does not currently bridge the two mechanisms automatically.
Connection pool behavior on error: All save and load operations release connections safely. On error, the connection is closed by
CleanupCommand(or byCommandBehavior.SingleRowwhen theRETURNINGreader was opened). No explicitROLLBACKis issued — that responsibility belongs to the transaction scope owner. This avoids the class of bugs where a child insert failure would abort an otherwise recoverable transaction.
⚠️ Hierarchical Save
Hierarchical parent-child saves work correctly on PostgreSQL when using the pattern that EntitySpaces was designed for — adding the child to the parent's collection before calling parent.Save():
var order = new Orders
{
CustomerId = "ALFKI",
EmployeeId = 4,
ShipVia = 1,
OrderDate = DateTime.Now
};
// Recommended — add to the parent's collection
order.OrderDetailsCollectionByOrderId.Add(new OrderDetails
{
ProductId = 10,
UnitPrice = 15.50f,
Quantity = 5,
Discount = 0.00f
});
order.Save(); // saves the parent first, then each child with the correct FK
AddNew() works the same way:
var detail = order.OrderDetailsCollectionByOrderId.AddNew();
detail.ProductId = 11;
detail.UnitPrice = 10.00f;
detail.Quantity = 2;
detail.Discount = 0.00f;
order.Save();
The child-initiated pattern (save from the child with UpTo set) is also supported:
var detail = new OrderDetails { ProductId = 1, UnitPrice = 25.00f, Quantity = 3, Discount = 0f };
detail.UpToOrdersByOrderId = order;
detail.Save(); // saves the parent first to obtain the PK, then the child
Atomicity — implicit transaction
esEntity.Save() automatically wraps hierarchical saves in an esTransactionScope when the entity has any pre-saves, post-saves, or post-one-saves (i.e. NeedsTransactionDuringSave() returns true). On any exception during the save phases, the scope is rolled back — the parent insert is reverted along with the failed child. No explicit esTransactionScope is required for the common hierarchical case.
For single-entity saves without hierarchy, no scope is created; the operation is a single SQL statement and therefore inherently atomic.
An explicit esTransactionScope around a hierarchical save is optional but recommended when multiple independent entities must succeed or fail together — the framework's internal scope joins the ambient one transparently via Required semantics.
Non-identity parent PKs
FK propagation from parent to child works identically for auto-increment PKs and user-supplied PKs:
- Auto-increment parent —
Orders.OrderId(GENERATED BY DEFAULT AS IDENTITY): the parent is inserted, the generated identity is retrieved viaRETURNING, and the FK is written into the child before the child's INSERT. - User-supplied parent —
Customers.CustomerId(VARCHAR(5), no identity): the parent PK is already known before the INSERT, so the FK is available immediately and the parent-child ordering is still preserved for referential integrity. - Explicit value on IDENTITY parent —
Orders.OrderIdassigned explicitly by the application: PostgreSQL accepts the explicit value onGENERATED BY DEFAULT AS IDENTITY; the provider routes it asInputOutputand includes it inRETURNING, and the FK is propagated the same way.
Both paths are exercised by the test suite against PostgreSQL 17 and 18, covering all five save patterns: persist-then-add, save-together, AddNew(), child-initiated via UpTo, and collection-initiated via collection.Save().
⚠️ Do not use bidirectional linking. Setting both
parent.Children.Add(child)andchild.UpToParent = parentcauses infinite recursion between the parent and child navigation properties. In practice this manifests as a hang or aStackOverflowExceptiondepending on the runtime. This is a limitation of the current EntitySpaces Core, not of this provider. Use one direction — the collection when saving from the parent, orUpTowhen saving from the child.
Collection Combine()
esEntityCollection<T>.Combine(source) moves entities from a source collection into a target collection. When the target has fks populated by a parent navigation getter (e.g. Customers.OrdersCollectionByCustomerId), the FK values are written into each combined entity's CurrentValues and marked as modified so they are included in the subsequent INSERT. The operation preserves the RowState of Unchanged entities whose FK already matches, and does not duplicate the FK entry in ModifiedColumns.
var source = new OrdersCollection();
var order = source.AddNew();
order.EmployeeId = 1;
order.OrderDate = DateTime.Now;
var target = customer.OrdersCollectionByCustomerId;
target.Combine(source);
target.Save(); // FK is written and persisted
Cross-provider parity
The fixes documented in this README were originally validated against SQL Server and ported to PostgreSQL without provider-specific changes. The following behaviours are now validated against both PostgreSQL 17 and PostgreSQL 18:
- Hierarchical save for identity and non-identity parent PKs.
- FK propagation across all five save patterns.
- Tolerant column/property name resolution, including divergent FK names.
- Combine() FK propagation and RowState preservation.
- Transaction scope, rollback, and atomicity.
- StoredProcedure path — insert, load, update, delete, collection save, optimistic concurrency.
- Optimistic concurrency enforced on both
UPDATEandDELETEthrough the SP template. - Dynamic SQL path uses single-statement
RETURNINGfor server-generated columns. - Concurrency columns (
IsConcurrencytimestamps) handled correctly on both INSERT and UPDATE, including multiple concurrency columns per table (up to six onbilling_invoice).
The SP suite is validated at 159 green tests on two consecutive runs against PostgreSQL 17 and PostgreSQL 18, with no provider-specific behaviour differences.
The Billing domain test suite (ported from SQL Server) accounts for 42 of those tests — Billing00–Billing06 (35 ported) plus Billing08 (7 additional: 6 green + 1 [Ignore]d for the known limitation documented above) — all validated against PostgreSQL 17.11.0 and PostgreSQL 18.6.0, with the pre-existing Northwind suite still green.
StoredProcedure Path
The provider invokes PostgreSQL functions via SELECT * FROM "proc_X"(:args), not via CommandType.StoredProcedure. Npgsql 7/8 translates CommandType.StoredProcedure to CALL, which PostgreSQL rejects with SQLSTATE 42883 when the target is a FUNCTION. The generated procedures use RETURNS SETOF "<table>" and RETURN QUERY SELECT (except Delete, which returns void) so the rowset carries the affected row back to the provider.
Generating the stored procedures
Run the PostgreSQL template in EntitySpaces Studio. It produces one <table>.sql per table plus a consolidated PostgreSQL_ALL.sql. Both must be applied with a client that respects dollar-quoting (psql, pgAdmin, DBeaver, Npgsql direct). Script splitters that break on ; inside $es$ ... $es$ are not supported — they produce truncated statements and spurious 42P13 errors.
Optimistic concurrency
Both proc_XUpdate and proc_XDelete enforce the EntitySpaces concurrency column:
Update increments and guards the row:
UPDATE "<table>"
SET "col" = p_col, "Version" = p_Version + 1
WHERE "id" = p_Id AND "Version" = p_Version;
A stale Version produces 0 affected rows; the function returns no rowset; the provider raises esConcurrencyException.
Delete guards the row:
DELETE FROM "<table>"
WHERE "id" = p_Id AND "Version" = p_Version;
GET DIAGNOSTICS v_rowcount = ROW_COUNT;
IF v_rowcount = 0 THEN
RAISE EXCEPTION 'esConcurrencyException: stale version on delete'
USING ERRCODE = 'P0001';
END IF;
The provider translates P0001 (matched by message prefix so unrelated RAISE sites are not misclassified) to esConcurrencyException. A stale Version on DELETE surfaces the same exception type as on UPDATE.
Special columns
DateAdded, DateModified, AddedBy, ModifiedBy are excluded from INSERT and UPDATE parameter lists. Server-side columns are populated by the DB DEFAULT; client-side columns by the framework's own SET logic before the SP call. RETURNING fetches the resulting values.
Required alignment
The provider's SP argument list and the function signature must match. Both are derived from the same column filters:
- INSERT: excludes
IsAutoIncrement,IsComputed, special columns. - UPDATE: excludes
IsComputed, special columns. - DELETE: includes PK columns plus the EntitySpaces concurrency column.
If you extend the SP template or the provider, keep both sides in sync. A signature mismatch produces 42883 at runtime.
Known limitations
The template matches special columns by hardcoded name (
DateAdded,DateModified,AddedBy,ModifiedBy) and the concurrency column by heuristic name (Version,RowVersion,VersionNumber). The Studio metadata engine does not expose these configs to templates. Renamed special or concurrency columns require extending the template.DROP FUNCTION IF EXISTS "name"(<types>)only matches a function whose signature matches exactly. When a template change alters the signature (for example the DELETE signature now includes the concurrency column), the old overload remains in the database and the new one is created alongside it. Clean up stale overloads before applying a regeneratedPostgreSQL_ALL.sql:DROP FUNCTION IF EXISTS "proc_invoiceDelete"(int); DROP FUNCTION IF EXISTS "proc_invoicedetailDelete"(int);Skipping this step causes
42883(function does not exist) or42725(ambiguous function call) at runtime.
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
PostgreSQL connection string:
esProviderFactory.Factory = new EntitySpaces.Loader.esDataProviderFactory();
esConnectionElement conn = new esConnectionElement();
conn.Provider = "EntitySpaces.PostgreSQLClientProvider";
conn.Schema = "public";
conn.ConnectionString = "Host=myserver;Port=5432;Database=mydb;" +
"Username=myuser;Password=mypassword;SSL Mode=Require;";
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
- Npgsql (>= 8.0.9)
-
net10.0
- Npgsql (>= 10.0.3)
- System.Configuration.ConfigurationManager (>= 10.0.14)
-
net8.0
- Npgsql (>= 10.0.3)
- System.Configuration.ConfigurationManager (>= 10.0.14)
-
net9.0
- Npgsql (>= 10.0.3)
- System.Configuration.ConfigurationManager (>= 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.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2026.11.0 | 82 | 11/11/2026 | |
| 2026.8.0 | 156 | 8/20/2026 | |
| 2026.7.0 | 141 | 7/23/2026 | |
| 2025.2.0 | 279 | 2/5/2025 | |
| 2025.1.0.4 | 227 | 1/30/2025 | |
| 2025.1.0.3 | 235 | 1/30/2025 | |
| 2024.8.0 | 310 | 8/18/2024 |
Actively maintained fork of the EntitySpaces ORM, modernized for .NET Framework 4.8 through .NET 10 and aligned with EntitySpaces Studio 2026.11.
PostgreSQL: INSERT ... RETURNING in a single round-trip, LATERAL translation for APPLY, concurrency exception translation (23505, 40001, 40P01, 55P03), PostgreSQL 13-18 and Npgsql 7-10, Neon compatible.
Full release notes: https://github.com/paulcordova/EntitySpaces/blob/master/CHANGELOG.md