Relatude Help

Options and events in Program.cs

ServerOptions holds what only code can say: file converters, folder paths, how settings are loaded, and the lifecycle callbacks.

builder.AddRelatudeDB(options => {
    options.FileConverters.Add(new SkiaImageConverter(1)); // optional: tried before the built-in converters
    options.DefaultDataFolderPath = "data";                // relative to the content root, or absolute

    // applied at every start, locked on the settings pages, never saved to a file
    options.OnServerSettingsInit = s => s.TokenCookieName = "MyToken";

    options.OnDatamodelInit = (dm, database) => dm.AddNamespace<IVenue>();
    options.OnStoreInit = db => db.RegisterTransactionPlugin(new AuditPlugin());
    options.OnStoreOpenBackground = db => Seeder.SeedIfEmpty(db);
});
Option What it does
FileConverters Your own file converters, tried before the built-in image and video converters.
DefaultDataFolderPath The root data folder. Default: the content root.
DefaultTempFolderPath The scratch folder, emptied at every start. Default: relatude.data.temp.
SettingsLoader An ISettingsLoader that replaces relatude.db.json.
ConfigurationSectionName The configuration section merged over the settings. Default RelatudeDB; null turns it off.
UseSettingsOverridesFile false makes the settings pages write relatude.db.json instead of the overrides file.
SettingsOverridesFilePath Keeps relatude.db.overrides.json at this path instead of with the default database.
AddCustomSettings(…) Registers a set of custom settings (above).
CreateUrlManager Makes each database's URL manager (§18).

The callbacks run in this order:

Callback When Use it for
OnServerSettingsInit after the settings are read and merged server settings set by code, the database list
OnContainerSettingsInit per database storage providers, file stores, model sources
OnStoreSettingsInit per database the engine settings (LocalSettings)
OnDatamodelInit after the model sources have loaded adding types from code
OnStoreInit store created, not yet open transaction plugins, task runners
OnStoreOpen store open; the open waits for it light work only
OnStoreOpenBackground store open, on a background thread seeding, warm-up
OnStoreClose host shutdown cleanup

Two things to know before you rely on them:

  • The server catches every exception in a callback. It writes the exception to the startup log and carries on. If a callback seems to do nothing, look there.
  • Seed in OnStoreOpenBackground. OnStoreOpen holds up the open, and while a store opens every request gets a 503 progress page.

AddRelatudeDB can be called any number of times. Every call configures the same ServerOptions, so a library and the host can each call it, in any order. A second UseRelatudeDB on the same application does nothing. There is one server per process, so a second application throws.

A library hooks into the start with services, not callbacks. The callbacks belong to the host: a host that assigns OnStoreInit after a library has chained onto it drops the library's part. A library registers these services instead (namespace Relatude.DB.NodeServer):

Service Called Use it for
IDatamodelInitializer.Initialize(datamodel, database) for every datamodel, right after OnDatamodelInit the library's model types
IStoreInitializer.Initialize(store) for every store before it opens, right after OnStoreInit transaction plugins, task runners, jobs
IStoreOpenHandler.OpenedAsync(store) for every store once open, in the background, beside OnStoreOpenBackground seeding, work that needs an open store
public sealed class AuditInitializer : IStoreInitializer {
    public void Initialize(NodeStore store) => store.RegisterTransactionPlugin(new AuditPlugin());
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IStoreInitializer, AuditInitializer>());

Open handlers run one after another, in the order they were registered. If one throws, the exception is logged and the next one still runs. The admin UI also builds datamodels to check a draft model, which runs the datamodel initializers too, so an initializer should only add types.