Entity Contracts
Serenity uses marker interfaces (contracts) to give a row special capabilities. By implementing one of these interfaces, your row tells the framework "I have an ID field", "I support soft delete", "I have logging fields", etc. — and the corresponding behaviors, handlers, and UI features activate automatically.
Identity & Name
- IIdRow — the row has an ID field. Put
[IdProperty]on the corresponding field. This is the most common contract; most rows implement it. - INameRow — the row has a name/description field. Put
[NameProperty]on the corresponding field. Used for the row's display title and quick search. - IDisplayNameRow — exposes a
DisplayNameField(aStringField).
public sealed class PersonRow : Row<PersonRow.RowFields>, IIdRow, INameRow
{
[DisplayText("Person Id"), Identity, IdProperty]
public int? PersonId { get => fields.PersonId[this]; set => fields.PersonId[this] = value; }
[DisplayText("Full Name"), NameProperty]
public string? FullName { get => fields.FullName[this]; set => fields.FullName[this] = value; }
}
Active / Deleted (Soft Delete)
- IIsActiveRow — the row has an
IsActiveField(Int16Field).1= active,0= inactive. - IIsActiveDeletedRow — an
IIsActiveRowwhere-1means deleted (soft delete via the active flag). - IIsDeletedRow — the row has an
IsDeletedField(BooleanField) for soft delete.
Rows implementing these are not physically deleted — the delete handler sets the flag instead, and list queries filter out deleted rows.
Logging Fields
- IInsertDateRow — has an
InsertDateField. - IInsertUserIdRow — has an
InsertUserIdField. - IUpdateDateRow — has an
UpdateDateField. - IUpdateUserIdRow — has an
UpdateUserIdField. - IInsertLogRow — combination of
IInsertDateRow+IInsertUserIdRow. - IUpdateLogRow — combination of
IUpdateDateRow+IUpdateUserIdRow. - ILoggingRow — combination of
IUpdateLogRow+IInsertLogRow. This is the one you implement for full insert/update audit logging. - IDeleteLogRow — has
DeleteUserIdFieldandDeleteDateField(delete audit logging).
The UpdateInsertLogBehavior fills these fields automatically on save. See Built-in Service Behaviors.
The user ID fields (
InsertUserIdField/UpdateUserIdField) are plainFieldobjects and their properties areobject-typed, so the user ID type follows the configured UserEntityOptions (int, long, Guid or string). Generated client typings showInsertUserId?: any.InsertDate/UpdateDateon the sharedLoggingRoware stored in UTC.
Ordering & Hierarchy
- IDisplayOrderRow — has a
DisplayOrderField(Int32Field); the row is kept in order automatically. - IParentIdRow — has a
ParentIdField; used for tree/hierarchical rows and parent validation.
Other Contracts
- IEmailRow — exposes an
EmailField(used by membership/account features). - IPasswordRow — exposes
PasswordHashFieldandPasswordSaltField(used by authentication). Password hash/salt fields are automatically protected (see Sensitive Field Protection). - IConcurrencyVersionRow — exposes a
ConcurrencyVersionField(rowversion / version) used for optimistic concurrency on save. See Optimistic Concurrency. - IFieldWithJoinInfo — a field that exposes its referenced aliases and the row's joins (used by query building).
How Contracts Are Used
These interfaces are what make the framework's conventions work:
- Request handlers check for
IIdRowto know the ID field,INameRowfor the name field,IIsActiveRow/IIsDeletedRowfor soft delete,ILoggingRowfor audit fields. - Behaviors activate based on these interfaces (e.g.
CaptureLogBehaviorrequiresIIdRow;UpdateInsertLogBehavioractivates for logging rows). - The UI uses
IIdRow/INameRowfor dialog titles, quick search, and lookup scripts.
Implementing a contract is usually just adding the interface to the row declaration and marking the corresponding field with the matching attribute ([IdProperty], [NameProperty], etc.).