Entity CRUD & Query Helpers
Beyond the request handlers, Serenity provides low-level extension methods to perform entity CRUD and query operations directly on a connection. These are useful when you need to work with rows outside of a service handler (e.g. in a migration, a background job, or a custom handler).
Important: These methods operate at a low level. They do not run service behaviors or perform service-level validation (permissions, required fields, etc.). For full service behavior, use the request handlers instead.
Reading Entities
EntityConnectionExtensions provides methods to read entities directly from a connection:
using var connection = sqlConnections.NewFor<MyRow>();
var row = connection.ById<MyRow>(someId); // throws if not found
var row2 = connection.TryById<MyRow>(someId); // returns null if not found
var first = connection.First<MyRow>(criteria); // first matching record
var single = connection.Single<MyRow>(criteria); // exactly one record
ById/TryById— find by ID (selects table fields only by default; overloads accept aneditQuerycallback to select more).First/TryFirst/Single/TrySingle— find by a criteria.List<TRow>— list all matching records.Count— count matching records.
These use the row's IIdRow/INameRow contracts and the fluent SQL query builder under the hood.
Writing Entities
The same class provides insert/update/delete helpers:
using var connection = sqlConnections.NewFor<MyRow>();
var row = new MyRow { Name = "Test" };
var newId = connection.InsertAndGetID(row); // insert, return new identity
row.Name = "Updated";
connection.UpdateById(row); // update by ID
connection.DeleteById<MyRow>(newId); // delete by ID
Insert/InsertAndGetID— insert a row (returns the new ID for identity columns).Update/UpdateById— update a row (by ID or by criteria).Delete/DeleteById— delete a row.
These operate on the row's assigned fields (assignment tracking), so only the fields you set are included in the SQL.
Query Building Helpers
EntityQueryExtensions
EntityQueryExtensions provides fluent helpers for building queries from a row:
WhereEqual(row)— adds all assigned field values to the WHERE clause with equality.Set(row)— sets all assigned field values (forSqlUpdate/SqlInsert).SelectTableFields(row)— selects the row's actual table fields.
These require the row to be in TrackAssignments mode.
EntitySqlHelper
EntitySqlHelper provides helpers to execute a query and load results into a row:
GetFirst(query, connection)— loads the first row into the query's loader row.GetSingle(query, connection)— loads the single row (throws if more than one).ForEach(query, connection, callback)— executes the callback for each row.
EntitySqlQueryExtensions
EntitySqlQueryExtensions provides additional query-building extensions for entities.
Field Helpers
EntityFieldExtensions provides helpers for working with fields:
IsTableField(field)— whether the field is an actual table column (not foreign/calculated/not-mapped).EnumerateTableFields(row)/GetTableFields(row)— the row's actual table fields.
Row Helpers
RowExtensions provides row-level helpers:
Clone()— clones a row.ApplyDefaultValues()— applies field default values.FindField()/FindFieldByPropertyName()— find a field by name.
Aliased Fields
AliasedFields lets you create an aliased copy of a row's fields for queries:
var f = MyRow.Fields.As("x");
// f.Name now refers to x.Name instead of T0.Name
This is useful when a query joins the same table more than once.
OptionalValue<T>
OptionalValue<T> is a small struct that wraps a value with a HasValue flag. It's used by interceptors to indicate whether a result is meaningful or whether the operation should continue normally.
IRowOperationInterceptor
IRowOperationInterceptor lets you intercept the entity CRUD operations performed through EntityConnectionExtensions (find, list, count, insert/update/delete). It's primarily implemented by mock connections in tests to avoid hitting a real database.