Partial Scripts & Resources (MIQS two-part scripts)
Updated
Two-part scripts#
A user script that declares a partial class is compiled together with a
generated framework part — the MIQS pattern of "generated shell + editable
business logic", adapted to the web stack. The framework is deterministic and
never stored: RuntimeCompiler.CompileAsync regenerates it on every
compile, so platform upgrades reach all scripts at once.
The pairing is implemented in ZuloOne.Runtime/ScriptFramework.cs:
GlobalUsings.cs— C#10global usingforSystem.*, allZuloOne.Runtime.*namespaces,ZuloOne.Core.Servicesand (when the generated-entities assembly is referenced)ZuloOne.Runtime.Generated. User partials carry no using block at all.Framework.cs— apartial class Xcounterpart for every partial class in the user source, emitted into the same namespace (parsed from the user code — event handlers stay inZuloOne.Runtime.Generatedby contract). It carries the shared helpers, currentlyGetResource(key, culture?).script.cs— the user code, path preserved so compile errors and PDB stack frames still point at what the user typed.
The user part owns the base-type declaration (public partial class Handler : CommandHandlerBase), which keeps the framework template universal across all
script types (event handlers, transactions, commands, services, reports, jobs,
tests, print forms). All starter templates in the frontend produce this form.
Scripts without partial compile exactly as before — the framework is
skipped, so ad-hoc/legacy full classes keep working.
The code editor (CodeEditorWorkbench, partialTools) shows three main tabs:
Script text | Framework | Resources. Framework is fetched from
POST /api/metadata/scripts/framework (read-only preview of the generated
part for the current code).
Resources#
Per-script localized texts (MIQS ScriptResource / ScriptID): MetaResources
table (ScriptMetaId = owning script or integration test, Name unique
inside that owner, Value default/EN, ValueRu, ValueAr). Unscoped rows
(ScriptMetaId null) remain on Developer → Resources and are the fallback.
- CRUD:
/api/metadata/resources?scriptId=(the Resources tab of the open script) and/api/metadata/resources(unscoped catalogue). - Runtime store:
ZuloOne.Runtime.Resources.ResourceStore— process-wide map reloaded byResourceStoreLoaderat startup and after every change; lookup is owned key → unscoped key → the key itself. Culture fallback: requested → default → the key. - Request language: the SPA sends
X-ZuloOne-Lang; middleware inProgram.cssetsResourceStore.CurrentLanguage(AsyncLocal), soUserMessages-style texts localize per user. - Scripts read resources two ways:
GetResource("Key")— framework helper, any string key;Res.Key— strongly-typed class generated into the entities assembly byMetadataCompilerService.CompileAllAsync(ScriptFramework.BuildResourceClass). Text edits are live (values resolve through the store); only new keys need a metadata recompile to appear onRes.
Tests: ZuloOne.Tests/PartialScriptsTests.cs, e2e frontend/e2e/resources.spec.ts.