Class SingletonSettingsCreationEngine<TSettings, TInput, TResult>
Template-method engine shared by every "create or update the ONE project-wide settings asset of this module" wizard: identity validation, target validation (folder shape and path collision), the transaction-backed creation with rollback, Addressables registration under the module's label, and the in-place update that restores the asset when a step after it fails. A concrete engine supplies only what genuinely differs between modules — its label, its default asset name, how it reads in a message, and which fields the input writes onto the asset.
Extracted from the ten near-identical *SettingsCreator classes (MenuServiceSettingsCreator
and ModalServiceSettingsCreator matched 85-94% line for line; five of the ten declared a
byte-identical ResolveNewAssetPath). ModalCreator is deliberately not one of them: it
authors many drafts, not one singleton, and belongs to
ResolveOrCreateByIdInFolder<TSettings>(string, string, string, SerenityAssetCreationTransaction, out bool)'s shape instead.
Adopt it the way BatchSoundEffectImporter adopts
BatchAudioAssetImportEngine{TItem,TInput,TResult,TAsset}: keep the module's
public static class as the caller-facing surface, hold one private sealed class Engine
instance behind it, and forward each public member in one line — the windows and tests that already
call the static creator never change.
The Id and the Guid are the engine's business, not a subclass's: it writes Id on both the create and the update path and seeds a fresh Guid exactly once, on creation. ApplyFields(TInput, TSettings) is therefore only ever handed the module's own fields to write, which is why the same override serves both paths.
Inherited Members
Namespace: Serenity.StructuredPersistence.Infrastructure.Editor.Authoring
Assembly: Serenity.UnityStructuredPersistence.Infrastructure.Editor.dll
Syntax
public abstract class SingletonSettingsCreationEngine<TSettings, TInput, TResult> where TSettings : ScriptableObject, IFoundationSettings where TInput : SingletonSettingsInput where TResult : SingletonSettingsResult, new()
Type Parameters
| Name | Description |
|---|---|
| TSettings | The module's concrete settings asset type. |
| TInput | The module's concrete request type. |
| TResult | The module's concrete outcome type. |
Constructors
SingletonSettingsCreationEngine()
Declaration
protected SingletonSettingsCreationEngine()
Properties
AssetTypeDisplayName
What the asset is called in a message an author reads, e.g. "Menu Service settings" — it completes "Assign a folder inside 'Assets' for the new … asset." and the Undo label.
Declaration
protected abstract string AssetTypeDisplayName { get; }
Property Value
| Type | Description |
|---|---|
| string |
DefaultAssetName
File name (without extension) used when the request names none, or names one that sanitizes away entirely.
Declaration
protected abstract string DefaultAssetName { get; }
Property Value
| Type | Description |
|---|---|
| string |
FailureMessagePrefix
The subject of the rollback sentence, e.g. "Menu service settings" for "Menu service settings generation failed and was rolled back: …". Separate from AssetTypeDisplayName because the two read in different positions and the ten creators this replaces do not capitalize them alike.
Declaration
protected abstract string FailureMessagePrefix { get; }
Property Value
| Type | Description |
|---|---|
| string |
ModuleLabel
The Addressables module label stamped on everything this engine creates.
Declaration
protected abstract string ModuleLabel { get; }
Property Value
| Type | Description |
|---|---|
| string |
Methods
ApplyFields(TInput, TSettings)
Writes the module's own fields from the request onto the asset. Not the Id and not the Guid — the engine owns both. Called directly by the create path; the update path calls ApplyFieldsToExisting(TInput, TSettings) instead, which forwards here by default.
Declaration
protected abstract void ApplyFields(TInput input, TSettings settings)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| TSettings | settings |
ApplyFieldsToExisting(TInput, TSettings)
Writes the request onto an asset that already exists. Defaults to ApplyFields(TInput, TSettings) — override only when an update must treat a field differently from a create, e.g. a collection field that can be authored outside the wizard and so must be merged into the existing asset's value on update rather than overwritten the way a fresh asset's field is simply assigned.
Declaration
protected virtual void ApplyFieldsToExisting(TInput input, TSettings settings)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| TSettings | settings |
Create(TInput)
Validates the request, then either updates the asset named by ExistingSettingsAssetPath or creates a brand-new one. Either way a failure leaves the project as it found it: a created asset is deleted, and an existing one edited before the failure is put back.
Declaration
public TResult Create(TInput input)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input |
Returns
| Type | Description |
|---|---|
| TResult |
CreateExtraAssets(TInput, TSettings, SerenityAssetCreationTransaction, TResult)
Assets a module creates alongside its settings singleton, inside the same transaction so
one failure rolls the whole run back. Track every created path with
Serenity.Global.Infrastructure.Editor.SerenityAssetCreationTransaction.TrackCreatedAsset(System.String), and every pre-existing
asset edited on the way with
SerenityAssetCreationTransaction.TrackExistingAsset.
Declaration
protected virtual void CreateExtraAssets(TInput input, TSettings settings, SerenityAssetCreationTransaction transaction, TResult result)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| TSettings | settings | |
| SerenityAssetCreationTransaction | transaction | |
| TResult | result |
CreateNewWithin(TInput, SerenityAssetCreationTransaction)
The part of a creation run that happens inside a transaction: ensures the output folder, creates the asset with its Id, a fresh Guid and the module's own fields, and verifies it landed on disk — without committing or rolling back anything itself. Public so a module's larger creator can fold the singleton's creation into ITS transaction alongside its other assets, rather than have this engine commit a separate one first. The created asset is at ResolveNewAssetPath(TInput), which is also the transaction's Serenity.Global.Infrastructure.Editor.SerenityAssetCreationTransaction.LastCreatedPath until the caller tracks something else.
Declaration
public TSettings CreateNewWithin(TInput input, SerenityAssetCreationTransaction transaction)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| SerenityAssetCreationTransaction | transaction |
Returns
| Type | Description |
|---|---|
| TSettings |
ResolveNewAssetPath(TInput)
The path a brand-new asset would be created at. Pure — computes the path without resolving, creating or ensuring anything, so a window's read-only preview and CreateNewWithin(TInput, SerenityAssetCreationTransaction) both read one formula.
Declaration
public string ResolveNewAssetPath(TInput input)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input |
Returns
| Type | Description |
|---|---|
| string |
UpdateExistingInPlace(TInput)
Updates the already-validated asset named by ExistingSettingsAssetPath in place. Public so a module's larger creator can update the singleton it resolved itself, without paying for a second Validate(TInput) pass over a request it already validated.
Declaration
public TResult UpdateExistingInPlace(TInput input)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input |
Returns
| Type | Description |
|---|---|
| TResult |
Validate(TInput)
Full validation, re-checked at creation time so Create(TInput) is safe to call independently of a window's live validation.
Declaration
public SerenityCreationValidationResult Validate(TInput input)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input |
Returns
| Type | Description |
|---|---|
| SerenityCreationValidationResult |
ValidateBeyondIdentity(TInput, SerenityCreationValidationResult)
Validation beyond identity that must NOT leak into ValidateIdentity(TInput)'s live, folder-less check: a value rule that reads other fields (a bounds check, a duplicate-name scan) or a non-blocking warning about an optional reference. Runs only inside Validate(TInput), once identity itself passed — skipped when it did not, so a full validation never reports a module error on top of an identity error.
Declaration
protected virtual void ValidateBeyondIdentity(TInput input, SerenityCreationValidationResult result)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| SerenityCreationValidationResult | result |
ValidateExtra(TInput, SerenityCreationValidationResult)
Validation beyond identity: the module's own fields (a controller id, a backend choice). Runs inside ValidateIdentity(TInput) — and therefore inside Validate(TInput) too, since Validate(TInput) begins by calling ValidateIdentity(TInput) — so a window's live identity check sees it as well as a full validation. Contrast ValidateBeyondIdentity(TInput, SerenityCreationValidationResult), which runs only on a full Validate(TInput).
Declaration
protected virtual void ValidateExtra(TInput input, SerenityCreationValidationResult result)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input | |
| SerenityCreationValidationResult | result |
ValidateIdentity(TInput)
Id-only validation, usable before a folder choice has been made.
Declaration
public SerenityCreationValidationResult ValidateIdentity(TInput input)
Parameters
| Type | Name | Description |
|---|---|---|
| TInput | input |
Returns
| Type | Description |
|---|---|
| SerenityCreationValidationResult |