Skip to content

Data · Types

Customers

The same CRUD shape as Catalog, aimed at the values that historically broke on a WASM transport: a Guid public id, a DateOnly birth date, a bool flag stored as INTEGER, and a decimal credit limit that keeps its scale because it is compared through ef_compare, not floating point.

Client database OPFS

Fastest persistent tier. Real files in the origin private file system. Concurrent connections, no COOP/COEP.

Directory

Every customer, sorted by name. Click a row to edit it in the panel under the table, next to what each value becomes in the SQLite file.

Try Save a customer with an email that already exists. The unique index is SQLite's, so the failure arrives as a DbUpdateException - see the snippet below.
Live
NameEmailBornCreditVIPActions

Edit customer

Select a row, or create one.

What the values become

PropertyStored as
PublicIdTEXT, 36-char Guid
DateOfBirthTEXT yyyy-MM-dd
IsVipINTEGER 0/1
CreditLimitTEXT, compared via ef_compare
CreatedUtcTEXT, ISO 8601

Filtering on CreditLimit - a real decimal comparison in SQL, not in the CLR - is on the SQL page.

csharp Reading and inserting
_rows = await context.Customers
    .AsNoTracking()
    .OrderBy(c => c.DisplayName)
    .ToListAsync();

// Insert: PublicId is a Guid the CLR generated, stored as TEXT and read back as a Guid.
context.Customers.Add(new Customer
{
    DisplayName = "Ada Lovelace",
    Email = "[email protected]",
    DateOfBirth = new DateOnly(1980, 1, 1),
    CreditLimit = 1_000m,
    IsVip = true,
});

await context.SaveChangesAsync();

The entity and its configuration

No browser-specific attributes, no converters bolted on for WASM. Precision and the unique index are declared once and apply on the server and in the tab alike.

csharp Customer.cs
public sealed class Customer
{
    public int Id { get; set; }
    public Guid PublicId { get; set; } = Guid.NewGuid();
    public string DisplayName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public DateOnly DateOfBirth { get; set; }
    public bool IsVip { get; set; }
    public decimal CreditLimit { get; set; }
    public string Notes { get; set; } = string.Empty;
    public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
}

// Precision travels with the model, not with the backend.
builder.Property(c => c.CreditLimit).HasPrecision(18, 2);
builder.HasIndex(c => c.Email).IsUnique();

Handling a constraint failure

After a failed save the change tracker still holds the rejected entity, so the demo clears it before the next read - the same hygiene you would apply on a server. The inner exception carries SQLite's message and result code.

csharp Pages/Customers.razor
An unhandled error has occurred. Reload 🗙