Engineering reference · 01

One entity.
One secure GridView.

The application author declares business meaning once. cwe-gen uses Roslyn to emit the repeatable server and client contract, and the UI binds one typed operations interface instead of rebuilding CRUD, transport, and authorization by hand.

Application code stays small Generated C# stays visible Server remains authoritative
Entities/Hr/Department/Department.csYou author this
[CxEntity]
[CxDisplayTemplate("{Name}")]
[CxAIEntity(Searchable = true)]
public partial class Department
{
    [Required, StringLength(100)]
    public string Name { get; private set; }

    public bool IsActive { get; private set; }
}
$ dotnet cwe-genRoslyn → deterministic files
00 · THE WHOLE PATH

Business intent crosses one governed pipeline.

The important split is ownership. Entity and page intent belong to the application. Repeated infrastructure belongs to the generator. Security remains a server decision even when the client reflects it.

Five-stage CodingWithEase pipeline from a developer-authored entity through cwe-gen to server rails, typed operations, and a GridView.
Entity to GridViewOpen for the full-resolution architecture.
01

Declare

Entity shape, validation, display metadata, CRUD mask, commands, queries.

02

Analyze

Roslyn reads types and members; diagnostics reject ambiguous or unsafe declarations.

03

Generate

DTOs, strong ids, handlers, endpoints, permissions, operations, AI descriptors.

04

Bind

The page injects IDepartmentOperations; the correct host implementation is selected.

05

Prove

Build, tests, composition analysis, regeneration, and clean-tree convergence.

01 · APPLICATION SOURCE

Start with the business object—not controllers.

Attributes state validation, editing, display, hierarchy, and AI discoverability next to the property they describe. Domain methods keep business transitions explicit.

What the engineer owns

This is normal C# under the Server project’s Entities/ area. It is the durable source of truth and the only place changed when the business object evolves.

  • Strongly expressed validation and display intent.
  • Private setters and domain methods protect state transitions.
  • Optional parent relation makes the same model usable in a tree grid.
  • CxAIEntity opts the record into permission-gated AI search.
Department.cs · abridged from the current starterAUTHORED
[CxEntity]
[CxDisplayTemplate("{Name}")]
[CxAIEntity(
    Summary = "A department in the company.",
    Searchable = true,
    SearchRoute = "/departments")]
public partial class Department
{
    [NonEditable]
    public DepartmentId Id { get; private set; }

    [Required(ErrorMessage = "Department name is required.")]
    [StringLength(100)]
    [Display(Name = "Name", Order = 10)]
    public string Name { get; private set; } = "";

    [Display(Name = "Parent department")]
    public DepartmentId? ParentId { get; private set; }

    [DefaultValue(true)]
    public bool IsActive { get; private set; } = true;

    public void Rename(string name) => Name = name;
    public void Deactivate() => IsActive = false;
}
Boundary

Framework/ is generated output. If generated behavior is wrong, change the entity, command, query, or generator—never patch a generated file.

02 · GENERATED BOUNDARY

One interface removes the server/browser split.

The contract and DTOs live where the browser can reference them. The Server implementation dispatches directly; the browser implementation performs typed HTTP calls. Components depend on neither.

IDepartmentOperations

The interface inherits the uniform CRUD surface used by connected UI components and also exposes entity-specific generated queries.

  • Server prerender receives DepartmentOperations.
  • WebAssembly receives DepartmentClientOperations.
  • The same Razor component works in both phases.
  • No page performs a fragile self-call with raw HttpClient.
Framework/Department/IDepartmentOperations.csGENERATED
public interface IDepartmentOperations
    : ICxCrudOperations<DepartmentViewModel,
        DepartmentEditModel, DepartmentId>
{
    Task<CxResult<DepartmentId>>
        CreateDepartmentAsync(DepartmentEditModel model, CancellationToken ct);

    Task<CxPagedResult<DepartmentViewModel>>
        GetDepartmentListAsync(CxQueryOptions options, CancellationToken ct);

    Task<DepartmentViewModel?>
        GetDepartmentByIdAsync(DepartmentId id, CancellationToken ct);

    Task<IReadOnlyList<CxAILookupCandidate>>
        FindDepartmentByNameAsync(string[] nameParts, int maxResults, CancellationToken ct);
}
03 · SERVER RAILS

Slim transport. Explicit dispatch. Security in the handler.

Endpoints authenticate the route group and delegate. The generated handler independently resolves the current caller’s effective permission set before touching the database.

DepartmentEndpoints.g.csGENERATED TRANSPORT
var group = endpoints
    .MapGroup(CxEntityCatalog.Department.Route)
    .WithTags("Department")
    .RequireAuthorization();

group.MapPost("/", async (
    DepartmentEditModel model,
    IDepartmentOperations operations,
    CancellationToken ct) =>
        await operations.CreateDepartmentAsync(model, ct));
DepartmentCreateCommand.g.csGENERATED ENFORCEMENT
var permissions =
    await permissionService.GetPermissionSetAsync(ct);

if (!permissions.IsGranted(DepartmentPermissions.Create))
    return CxResult<DepartmentId>.Failure(
        new CxError("forbidden", "Not authorized."));

var entity = Department.Create(request.DepartmentEditModel);
db.Set<Department>().Add(entity);
await db.SaveChangesAsync(ct);
Security

Hiding the New button is user experience. This handler check is the security boundary. Calling the endpoint manually or modifying client state cannot bypass it.

04 · CLIENT COMPOSITION

The GridView consumes business operations—not infrastructure.

The page declares columns, editing fields, and permission keys. Paging, sorting, searching, CRUD dialogs, typed results, and host selection are already owned by the connected component and generated contract.

The application-specific UI

This is where engineering judgment belongs: which facts matter, which actions make sense, and how the user edits the business object.

  • Columns are typed expressions, not field-name strings.
  • The edit form binds the generated DepartmentEditModel.
  • Permission constants gate client actions consistently.
  • The generated operations contract remains the only data dependency.
Components/DepartmentsGrid.razorAUTHORED UI
@inject IDepartmentOperations Departments

<CweEntityGrid
    TRow="DepartmentViewModel"
    TEdit="DepartmentEditModel"
    TId="DepartmentId"
    Operations="Departments"
    Title="Departments"
    CreatePermission="@DepartmentPermissions.Create"
    EditPermission="@DepartmentPermissions.Update"
    DeletePermission="@DepartmentPermissions.Delete">
  <Columns>
    <CweGridTextColumn Property="d => d.Name" />

    <CweGridBoolColumn TRow="DepartmentViewModel"
        Property="d => d.IsActive" Title="Status" />
  </Columns>
  <FormTemplate Context="m">
    <CweTextBox Label="Name" @bind-Value="m.Name" Required />

    <CweCheckbox Text="Active" @bind-Value="m.IsActive" />
  </FormTemplate>
</CweEntityGrid>
Generated

Strong contracts

Strong ids, view/edit models, endpoints, handlers, client operations, and permission catalogs.

Enforced

Permission boundary

Authenticated endpoints plus per-handler checks against the server-resolved caller.

Analyzed

Composition rules

Invalid component parameters, raw self-calls, missing validation, and unreadable grids surface early.

Verified

Convergence

Regeneration must leave the tracked generated tree unchanged; drift fails CI.

Next engineering page

How AI coding agents use capability truth instead of guessing.

Open AI coding agents →