-
Notifications
You must be signed in to change notification settings - Fork 0
Basic Syntax
This document outlines the basic syntax and structure of the DSL used for defining models, repositories, services, and components.
Data structures are defined using a class-like syntax, specifying the fields and their types.
In Account.model
:
schema {
int Id,
string Email = {""},
string FirstName = {""},
string LastName = {""},
...
}
-
schema
: Indicates this is the model type's primary column definition. -
int
,string
: Data types. Standard C# types are supported, and special values can be wrapped with{
and}
. -
= {...}
: Used to provide a default value expression, which can be any constant C# value
You can extend data structures with partial classes to add functionality or specific properties.
In Account.model
, specify a class Account.WithPassword
:
partial WithPassword {
string PasswordScheme = {""},
string PasswordHash = {""},
string PasswordSalt = {""},
DateTime? PasswordSet
}
- The
partial
keyword indicates an extension of a previously defined structure. - This allows you to add additional properties without modifying the base schema directly.
You can add an inner class to the model which does not inherit from the base model class.
In Account.model
, specify a class Account.LoginParams
:
dto LoginParams {
string Username = {""},
string Password = {""},
bool RememberMe = {true}
}
- The
dto
keyword indicates this is a DTO inner class with no super class. - This allows you to add additional DTO types closely related to the base schema.
Repositories define the methods used to access and manipulate data.
In Account.model
:
repo {
dbo.Account_GetById(int id)
=> Account,
dbo.Account_GetWithPassword(string email)
=> Account.WithPassword,
dbo.List_WithSubRecords_Json()
=> json List<Account.WithSubRecords>,
...
}
-
repo
: Indicates a repository definition. -
dbo.Account_GetById(int id)
: Defines a method namedAccount_GetById
which accepts anint
parameter. The generated function will replace.
characters with__
; the underlying logic is implemented in SQL Server as a stored procedure. -
=> Account
: Specifies the return type of the method (optional). -
json
: Denotes that the stored procedure will return JSON-formatted content (optional). -
Account.WithPassword
: Returns anAccount
object, extended with theWithPassword
partial class.
Stored procedures are executed by the default implementation of IModelDbAdapter
.
Services define the interface for business logic.
In Account.model
:
service {
AttemptLogin(string email, string password)
=> Account.WithSession,
LogOut(),
ResetPassword(string resetToken, string password),
...
}
-
service
: Indicates a service definition. -
AttemptLogin(string email, string password)
: Defines a method namedAttemptLogin
that accepts string parameters. -
=> Account.WithSession
: Specifies the return type of the method (optional).
A DI-injectable interface named Account.IService
is generated for both backend and frontend C# code. An interface is
also generated called Account.IBackendService
, which you must fully implement as a service class, then register for
DI in the backend Program.cs
.
In Program.cs
:
builder.Services.AddAccountBackend<MyAccountService>(); // Use backend services
// - or -
builder.Services.AddAccountFrontend(); // Use HTTP client services
In MyComponent.razor
or MyBackendService.cs
:
@inject Generated.Account.IService accountService
// - or -
public class MyBackendService(Generated.Account.IService accountService)
{
// ...
}
When service functions are called from the backend, the service implementation
provided for Account.IBackendService
will be called wrapped by the default implementation
of IModelDbWrapper
.
When called from the frontend (e.g., from Blazor WASM), the call is dispatched via a generated
HTTP client (through the default implementation of IModelApiAdapter
) to a generated
API controller, which in turn invokes the backend service implementation (at which point the
call is also wrapped by IModelDbWrapper
).
Reactive components define reusable UI elements. These are separated into state, actions, and body definitions.
-
state { ... }
: Defines the component's state variables.
state {
int count,
bool enabled = {true},
string firstName = {""},
...
}
-
interface { ... }
: Defines the actions the component can perform.
interface {
Increment(),
Decrement(),
Add(int amount),
...
}
- Body Definition: Defines the HTML structure of the component. Uses a fully parenthesized prefix notation, which allows attribute and parameter definitions for HTML elements and sub-components.
div(| class = {"d-flex flex-row gap-2"} |
div(| class = {"my-auto"} |
{"Count:" + count}
)
button(|
class = {"btn btn-danger"},
onclick = {"dispatch(this, 'Decrement');"}
|
<">-</">
)
button(|
class = {"btn btn-success"},
onclick = {"dispatch(this, 'Increment');"}
|
<">+</">
)
)
- The
|
characters are used to enclose attribute definitions within HTML tags and parameter lists for sub-components. - HTML attribute and sub-component parameter value expressions are wrapped with
{
and}
. - HTML attribute and sub-component parameters are C# expressions which can reference state.
-
dispatch(this, 'action', pars)
is a JS function used to trigger actions within the component, supporting passing action parameters as a JSON object. - Regular HTML elements are denoted using identifiers in lowercase, such as
button
andinput
. - Sub-components, defined in
.model
files, are denoted using capitalized names such asMyComponent
. - If your component has no
interface
entries, a default implementation namedMyComponent.Default
; register this as a DI-injectable service:
builder.Services.AddMyComponentView<MyComponentBase.Default>();
- If your component defines
interface
entries, fully implement theMyComponentBase
abstract class as a service class and inject that instead.
builder.Services.AddMyComponentView<MyComponentImpl>();
More elaborate syntax and control flows are available, including conditionals and loops. Refer to the specific documentation for using flow-control and understanding how the DOM syntax tree is assembled.