logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class ServiceExtensionCreator

    Generates a service extension for a discovered AggregateCatalog aggregate: the service class (fresh implementation, decorator, or infrastructure subclass), its DI wiring, an optional initialization-pipeline task, layered asmdefs, a test stub and a README — in one pass, with rollback on failure. Every generated aggregate mirrors the layout real Serenity aggregates use (see Unity/Score, Unity/PlayerPrefsPersistence): a code layer (Application or Infrastructure) under Services/, an Installation/ layer with Installers/ (and Settings/ for a PipelineReplace task asset), an optional Tests/ layer, and a root README.md — each layer owning its own folder and, unless Skip, its own asmdef. Mirrors the structure of CutsceneCreator / ModalCreator: this class only mutates the project; a future wizard window collects the ServiceExtensionCreator.Input.

    Inheritance
    object
    ServiceExtensionCreator
    Inherited Members
    object.ToString()
    object.Equals(object)
    object.Equals(object, object)
    object.ReferenceEquals(object, object)
    object.GetHashCode()
    object.GetType()
    object.MemberwiseClone()
    Namespace: Serenity.ServiceExtensionCreator.Editor
    Assembly: Serenity.ServiceExtensionCreator.Editor.dll
    Syntax
    public static class ServiceExtensionCreator
    Remarks

    Service generation (Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.CreateService(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input)) is the original V1 engine and is untouched by the Catalog V2 extension. Every other ExtensionPointKind is generated by Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.CreateExtensionPoint(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input), which reuses the same layered layout, the same Fresh/Decorator/Subclass class emitters (via Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.AdaptToAggregateDescriptor(Serenity.ServiceExtensionCreator.Editor.ExtensionPointDescriptor,System.Type), which feeds an ExtensionPointDescriptor into the exact same CreateServiceScript(AggregateDescriptor, Style, string, string, string, List<string>, bool) used for services) and the same rollback contract, with kind-specific wiring layered on top.

    Methods

    ComposeCodeLayerName(Input)

    The generated code layer's folder/namespace segment: "Infrastructure" or "Application" for the Service kind (see Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.ResolveServiceCodeLayerName(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input)); always "Infrastructure" for every other kind.

    Declaration
    public static string ComposeCodeLayerName(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeExtensionPointFolderSegment(Input)

    The generated code layer's kind-named subfolder for a Store/Repository/Gateway/Factory extension point ("Stores"/"Repositories"/"Gateways"/"Factories") — used for a live preview. Null for the Service kind (see ComposeCodeLayerName(Input) instead) and the Installer kind (no code layer class is ever generated for it).

    Declaration
    public static string ComposeExtensionPointFolderSegment(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeExtensionPointWiringTypeName(Input)

    The generated wiring subclass's type name for a Store/Repository/Gateway/Installer extension point — used for a live preview during the Wiring/Output wizard steps, mirroring how Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.CreateBackendWiring(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.BackendWiringContext,System.Collections.Generic.List{System.String},System.Collections.Generic.List{System.String},System.Collections.Generic.List{System.String}) and Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.CreateExtensionPoint(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input) derive the same name for real. Empty for the Service kind (its wiring name depends on the free-choice Wiring), the Factory kind (no wiring is ever generated for it), a Repository/Gateway extension point with no discoverable installer hook, and a bespoke Store contract with no backend hook (None).

    Declaration
    public static string ComposeExtensionPointWiringTypeName(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeRoot(Input)

    The composed extension root every generated layer folder/namespace/asmdef is named from — the composed type name minus its trailing kind suffix (e.g. "PostgresSQLPersistence" for a "PostgresSQL" extension of the Persistence service, "PostgresBlob" for a "Postgres" extension of the BlobStore extension point). Empty when ComposeServiceTypeName(Input) is.

    Declaration
    public static string ComposeRoot(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeRootNamespace(Input)

    The root namespace every generated layer's namespace is built from — NamespaceOverride when set, otherwise ComposeRoot(Input).

    Declaration
    public static string ComposeRootNamespace(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeSanitizedExtensionName(Input)

    Computes the sanitized extension name (PascalCase, "Unity"-stripped for Infrastructure style) that every generated file and type name is built from — used alongside ComposeServiceTypeName(Input) for a live preview of the wiring/test/README file names while the wizard is still collecting input.

    Declaration
    public static string ComposeSanitizedExtensionName(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeServiceTypeName(Input)

    Computes the generated service type name for the given input, without validating or creating anything — used for a live preview while the user is still filling in the wizard.

    Declaration
    public static string ComposeServiceTypeName(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ComposeTypeName(ExtensionPointDescriptor, Style, string)

    Composes the generated type name for any extension point kind. A Service-kind InfrastructureSubclass is named after its actual base ({Name}{BaseTypeName} off the — possibly rebased — descriptor's ConcreteType): byte-identical to V1's {Name}Unity{Aggregate}Service for the canonical base, base-aware for any other eligible base. Every other Service style keeps {Name}{Aggregate}Service. Every other kind is simply {Name}{ExtensionPointName} regardless of the chosen base — no "Unity" prefix (per the naming rule: PostgresBlobStore, {Name}ScoreRepository, {Name}AudioPlayerGateway, {Name}FilePersistenceDriversFactory, {Name}PersistenceInstaller — Name already IS that per-kind suffix, having been derived from the contract type's name). Public and pure so tests can pin the naming against a directly-constructed descriptor.

    Declaration
    public static string ComposeTypeName(ExtensionPointDescriptor descriptor, ServiceExtensionCreator.Style style, string sanitizedExtensionName)
    Parameters
    Type Name Description
    ExtensionPointDescriptor descriptor
    ServiceExtensionCreator.Style style
    string sanitizedExtensionName
    Returns
    Type Description
    string

    Create(Input)

    Validates the request, then generates every requested artifact in its layered folder. On any failure every created path is rolled back so the project is left untouched (mirrors CutsceneCreator / ModalCreator).

    Declaration
    public static ServiceExtensionCreator.Result Create(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    ServiceExtensionCreator.Result

    DescribeVirtualizationRequirement(ExtensionPointDescriptor)

    The rejection every generation surface shows for a not-yet-virtualized user class — shared by Validate(Input) and the wizard's step gating so the wording never drifts.

    Declaration
    public static string DescribeVirtualizationRequirement(ExtensionPointDescriptor descriptor)
    Parameters
    Type Name Description
    ExtensionPointDescriptor descriptor
    Returns
    Type Description
    string

    IsInfrastructureAvailable(AggregateDescriptor, out string)

    True when the InfrastructureSubclass style is available for descriptor — at least one eligible subclass base exists (any public, non-abstract, non-test implementer of I{X}Service with overridable members, not just the canonical Unity{X}Service — see EligibleSubclassBases). Shared by Validate(Input) and ServiceExtensionCreatorWindow so the rule and its wording never drift apart.

    Declaration
    public static bool IsInfrastructureAvailable(AggregateDescriptor descriptor, out string reason)
    Parameters
    Type Name Description
    AggregateDescriptor descriptor
    string reason
    Returns
    Type Description
    bool

    IsSubclassStyleAvailable(ExtensionPointDescriptor, out string)

    True when the Subclass style is available for descriptor — at least one eligible subclass base exists (see EligibleSubclassBases). Shared by Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.ValidateExtensionPointStyle(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input,Serenity.ServiceExtensionCreator.Editor.ExtensionPointDescriptor) and ServiceExtensionCreatorWindow so the rule and its wording never drift apart (mirrors IsInfrastructureAvailable(AggregateDescriptor, out string) for the Service kind). Never consulted for the Installer kind, which forces the Subclass style through its own dedicated abstract-base path — see Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.ValidateExtensionPointStyle(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input,Serenity.ServiceExtensionCreator.Editor.ExtensionPointDescriptor).

    Declaration
    public static bool IsSubclassStyleAvailable(ExtensionPointDescriptor descriptor, out string reason)
    Parameters
    Type Name Description
    ExtensionPointDescriptor descriptor
    string reason
    Returns
    Type Description
    bool

    ResolveEffectiveServiceDescriptor(Input, AggregateDescriptor)

    The descriptor the Service-kind pipeline actually generates against: for the InfrastructureSubclass style, the catalog descriptor rebased onto the resolved SubclassBaseTypeName (canonical/first when empty); for every other style, the catalog descriptor untouched. An unresolvable base name returns the descriptor untouched — Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.ValidateService(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.Input) already rejected it, so this fallback only ever feeds previews. Public so the wizard's asmdef-references preview reads the same rebased descriptor real generation will.

    Declaration
    public static AggregateDescriptor ResolveEffectiveServiceDescriptor(ServiceExtensionCreator.Input input, AggregateDescriptor descriptor)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    AggregateDescriptor descriptor
    Returns
    Type Description
    AggregateDescriptor

    ResolveUserClassDescriptor(IReadOnlyList<ExtensionPointDescriptor>, string, out string)

    Resolves a Class-kind extension point by simple or full type name over userClassCatalog. Unlike every other kind, two user classes may legitimately share a simple name across namespaces (Enemies.Spawner / Items.Spawner) — a simple name matching several classes is rejected with an actionable ambiguity error instead of silently first-picking, mirroring Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator.ResolveSubclassBase(System.Collections.Generic.IReadOnlyList{System.Type},System.String,System.String@)'s philosophy; a dotted name matches Type.FullName exactly. Public and list-driven so tests can pin the resolution rules against constructed descriptors.

    Declaration
    public static ExtensionPointDescriptor ResolveUserClassDescriptor(IReadOnlyList<ExtensionPointDescriptor> userClassCatalog, string requestedName, out string ambiguityError)
    Parameters
    Type Name Description
    IReadOnlyList<ExtensionPointDescriptor> userClassCatalog
    string requestedName
    string ambiguityError
    Returns
    Type Description
    ExtensionPointDescriptor

    Validate(Input)

    Returns a human-readable validation error, or null when the request is valid.

    Declaration
    public static string Validate(ServiceExtensionCreator.Input input)
    Parameters
    Type Name Description
    ServiceExtensionCreator.Input input
    Returns
    Type Description
    string

    ValidateSubclassBaseSelection(IReadOnlyList<Type>, string)

    Returns the resolver error a SubclassBaseTypeName selection would produce against eligibleBases, or null when it resolves — the wizard's per-step twin of the Validate(Input)-time check, shared so the rule and its wording never drift apart.

    Declaration
    public static string ValidateSubclassBaseSelection(IReadOnlyList<Type> eligibleBases, string requestedName)
    Parameters
    Type Name Description
    IReadOnlyList<Type> eligibleBases
    string requestedName
    Returns
    Type Description
    string
    In this article
    © 2026 Serenity. All Rights Reserved