Skip to content

Retrieve Request Handler

The RetrieveRequestHandlerAsync is the base class that handles Retrieve service requests — fetching a single record by ID. It's what edit dialogs use to load a record's details before showing the edit form.

From Serenity 10.5.0, Sergen generates asynchronous handlers (RetrieveRequestHandlerAsync) and endpoint actions by default. The synchronous RetrieveRequestHandler is kept for backward compatibility and is marked obsolete — it will be deprecated in a future version. New code — and code generated by Sergen — should use the async variants.

Generated Handler

For a Language entity, Sergen generates:

using MyRow = MyProject.Administration.LanguageRow;

namespace MyProject.Administration;

public interface ILanguageRetrieveHandler : IRetrieveHandlerAsync<MyRow> { }

public class LanguageRetrieveHandler(IRequestContext context)
    : RetrieveRequestHandlerAsync<MyRow>(context), ILanguageRetrieveHandler
{
}

The class derives from the generic RetrieveRequestHandlerAsync<TRow> and IRetrieveRequestHandler. Like the other handlers, it is auto-registered through the type source.

RetrieveRequestHandlerAsync<TRow> itself derives from the fully generic RetrieveRequestHandlerAsync<TRow, TRetrieveRequest, TRetrieveResponse> — the extra arguments let you customize the request and response types.

The Service Endpoint

The endpoint exposes a single Retrieve action:

[Route("Services/Administration/Language/[action]")]
[ConnectionKey(typeof(MyRow)), ServiceAuthorize(typeof(MyRow))]
public class LanguageEndpoint : ServiceEndpoint
{
    [HttpPost, AuthorizeRead(typeof(MyRow))]
    public Task<RetrieveResponse<MyRow>> Retrieve(IDbConnection connection, RetrieveRequest request,
        [FromServices] ILanguageRetrieveHandler handler, CancellationToken cancellationToken = default)
    {
        return handler.RetrieveAsync(connection, request, cancellationToken);
    }
}

[AuthorizeRead(typeof(MyRow))] validates the read permission declared on the row. The endpoint returns Task<RetrieveResponse<MyRow>> and forwards the CancellationToken to the handler.

The Request and Response

The request is a RetrieveRequest:

public class RetrieveRequest : ServiceRequest, IIncludeExcludeColumns
{
    public object? EntityId { get; set; }
    public RetrieveColumnSelection ColumnSelection { get; set; }
    public HashSet<string>? IncludeColumns { get; set; }
    public HashSet<string>? ExcludeColumns { get; set; }
}
  • EntityId — the ID of the record to fetch.
  • ColumnSelection — which columns to load (defaults to Details).
  • IncludeColumns / ExcludeColumns — explicit column overrides, like the list handler.

The response is a RetrieveResponse<T>:

public class RetrieveResponse<T> : ServiceResponse, IRetrieveResponse
{
    public T? Entity { get; set; }
    public Dictionary<string, T>? Localizations { get; set; }
}

Entity is the loaded record; Localizations holds per-language values when localization is used.

RetrieveColumnSelection

RetrieveColumnSelection controls which columns are loaded:

Value Meaning
Details (default) All table and view columns (except unmapped / complex columns)
KeyOnly Only primary key fields
List Only table columns (like ColumnSelection.List in the list handler)
None No columns by default
IdOnly Only the ID field
Lookup ID, name, and fields with [LookupInclude]

Lifecycle Methods

The main overridable methods on RetrieveRequestHandlerAsync are the async variants:

  • ValidateRequestAsync() — checks the read permission.
  • OnBeforeExecuteQueryAsync() — called before the retrieve query runs.
  • OnAfterExecuteQueryAsync() — called after the query succeeds.
  • OnReturnAsync() — called just before the response is returned.

Each accepts a CancellationToken cancellationToken = default and returns Task. Call the base implementation first when overriding.

These also invoke any registered retrieve behavior hooks (OnPrepareQueryAsync, OnBeforeExecuteQueryAsync, OnAfterExecuteQueryAsync, OnReturnAsync).

Behaviors

Behaviors implementing IRetrieveBehaviorAsync (or the sync IRetrieveBehaviorSync) run for every retrieve handler and are the recommended way to add cross-cutting retrieve logic (e.g. filtering what a user can read).

See Also