Context (CRUD Operations)¶
TTContext is the primary API entry point for all ORM operations. It is defined in Trysil.Context.pas.
Creating a Context¶
// Single connection (identity map enabled by default)
LContext := TTContext.Create(LConnection);
// Single connection with explicit identity map control
LContext := TTContext.Create(LConnection, True); // enabled
LContext := TTContext.Create(LConnection, False); // disabled
// Read/write split (identity map enabled by default)
LContext := TTContext.Create(LReadConnection, LWriteConnection);
// Read/write split with explicit identity map control
LContext := TTContext.Create(LReadConnection, LWriteConnection, False);
When using read/write split, SELECT operations go through the read connection and INSERT/UPDATE/DELETE operations go through the write connection. This supports primary/replica database topologies.
Read Operations¶
All read operations support entities with [TJoin] attributes -- the generated SQL automatically includes JOIN clauses and column aliases. See JOIN Queries.
SelectAll¶
Load all entities of a given type:
LPersons := TTList<TPerson>.Create;
try
LContext.SelectAll<TPerson>(LPersons);
for LPerson in LPersons do
WriteLn(LPerson.Firstname);
finally
LPersons.Free;
end;
Select (Filtered)¶
Load entities matching a filter:
LPersons := TTList<TPerson>.Create;
try
LContext.Select<TPerson>(LPersons, LFilter);
finally
LPersons.Free;
end;
See Filtering for how to build filters.
SelectCount¶
Count matching records without loading entities:
Get¶
Load a single entity by primary key. Returns nil if not found:
TryGet¶
Safe alternative that returns a Boolean:
Refresh¶
Reload an entity from the database, overwriting in-memory changes:
OldEntity¶
Get a snapshot of the entity as it exists in the database (before any in-memory changes):
LOld := LContext.OldEntity<TPerson>(LPerson);
try
if LOld.Lastname <> LPerson.Lastname then
WriteLn('Lastname changed');
finally
LOld.Free;
end;
OldEntity creates a clone and refreshes it, so the caller owns the returned object and must free it.
RawSelect¶
Execute arbitrary SQL and map results to typed DTO classes. DTO classes only need [TColumn] attributes -- [TTable], [TPrimaryKey], and [TSequence] are not required:
LResult := TTObjectList<TOrderSummary>.Create;
try
LContext.RawSelect<TOrderSummary>(
'SELECT c.CompanyName AS CustomerName, SUM(o.Amount) AS Total ' +
'FROM Orders o JOIN Customers c ON o.CustomerID = c.ID ' +
'GROUP BY c.CompanyName',
LResult);
finally
LResult.Free;
end;
Results are read-only and the identity map is not used. See Raw Select for details.
Write Operations¶
Insert¶
LPerson := LContext.CreateEntity<TPerson>();
LPerson.Firstname := 'John';
LPerson.Lastname := 'Smith';
LContext.Insert<TPerson>(LPerson);
// LPerson.ID is now assigned by the sequence
InsertAll¶
Wraps all inserts in a single transaction. If any insert fails, the entire batch is rolled back.
Update¶
UpdateAll¶
Delete¶
If the entity has a [TDeletedAt] column, Delete performs a soft delete (UPDATE) instead of a SQL DELETE. See Entity Mapping — Soft Delete for details.
DeleteAll¶
Save Operations¶
Save¶
Save automatically determines whether to insert or update. Entities created via CreateEntity<T> are tracked in an internal TTNewEntityCache and will be inserted. All other entities are updated.
LPerson := LContext.CreateEntity<TPerson>();
LPerson.Firstname := 'New';
LContext.Save<TPerson>(LPerson); // INSERT (tracked as new)
LPerson.Firstname := 'Updated';
LContext.Save<TPerson>(LPerson); // UPDATE (no longer in new cache)
SaveAll¶
ApplyAll¶
Execute inserts, updates, and deletes for separate lists in a single transaction:
The three lists are processed in order: inserts first, then updates, then deletes. If any operation fails, the entire transaction is rolled back.
Factory Methods¶
CreateEntity¶
Create a new empty entity. The entity is registered in the new-entity cache so that Save knows to insert it:
CloneEntity¶
Deep-clone an existing entity:
CreateTransaction¶
Create an explicit transaction. See Transactions:
CreateSession¶
Create a Unit of Work session. See Sessions:
CreateFilterBuilder¶
Create a fluent filter builder. See Filtering:
GetMetadata¶
Retrieve table metadata for an entity type:
CreateDataset¶
Execute raw SQL and return a TDataset. For most use cases, prefer RawSelect<T> which also handles mapping automatically:
Validation¶
Validate an entity explicitly before submitting. Raises ETValidationException on failure:
try
LContext.Validate<TPerson>(LPerson);
except
on E: ETValidationException do
ShowMessage(E.Message);
end;
Validation also runs automatically before every Insert and Update operation inside the resolver. See Validation for details.
Properties¶
| Property | Type | Description |
|---|---|---|
InTransaction |
Boolean |
Whether the write connection has an active transaction |
SupportTransaction |
Boolean |
Whether the write connection supports transactions |
UseIdentityMap |
Boolean |
Whether the identity map is enabled for this context |
OnGetCurrentUser |
TFunc<String> |
Callback that returns the current user name for change tracking *By fields |
OnGetCurrentUser¶
Assign this property to provide the current user name for change tracking attributes ([TCreatedBy], [TUpdatedBy], [TDeletedBy]):
If not assigned, an empty string is written to *By fields. See Entity Mapping — Change Tracking for details.
Typical Usage Pattern¶
LConnection := TTSQLiteConnection.Create('Main');
try
LContext := TTContext.Create(LConnection);
try
// Read
LPersons := TTList<TPerson>.Create;
try
LContext.SelectAll<TPerson>(LPersons);
for LPerson in LPersons do
WriteLn(Format('%s %s', [LPerson.Firstname, LPerson.Lastname]));
finally
LPersons.Free;
end;
// Write
LPerson := LContext.CreateEntity<TPerson>();
LPerson.Firstname := 'Alice';
LPerson.Lastname := 'Smith';
LContext.Insert<TPerson>(LPerson);
finally
LContext.Free;
end;
finally
LConnection.Free;
end;