ReadOnly Event
Summary
The ReadOnly event controls whether a property can be written to. When a read-only condition evaluates to true, any attempt to set the property throws an InvalidOperationException. Multiple read-only subscribers can exist on the same property — if any condition is true, the property is read-only.
When does it fire?
Read-only checks are evaluated when a property setter is called, before any other processing (AutoCorrect, Validate, etc.):
Set property → ReadOnly? → Yes → Throw InvalidOperationException
→ No → Continue (AutoCorrect → Validate → ...)
Read-only state is also evaluated when the API retrieves read-only metadata for the UI. This means read-only conditions are called frequently and must be extremely fast.
Syntax
salesOrderDetail.ReadOnly(d => d.Quantity)
.If(d => d.SalesOrder != null && d.SalesOrder.Status == SalesOrderStatus.Closed);
Fluent API
| Step | Method | Description |
|---|---|---|
| Required | .If(condition) |
Condition that, when true, makes the property read-only |
Scenarios
1. Locking the properties on a Sales Order when the order is finalized
Properties on the sales order are also locked once the order is finalized.
[Logic]
public class SalesOrderFinalizeBL(SalesOrder.Logic salesOrder, SalesOrderDetail.Logic salesOrderDetail)
{
[RegisterLogic]
public void ReadOnlyWhenFinalized()
{
salesOrder.ReadOnly(o => o.SellToCustomer).If(IsOrderFinalized);
salesOrder.ReadOnly(o => o.OrderDate).If(IsOrderFinalized);
salesOrder.ReadOnly(o => o.ShippingAmount).If(IsOrderFinalized);
salesOrderDetail.ReadOnly(d => d.Product).If(IsOrderFinalized);
salesOrderDetail.ReadOnly(d => d.Description).If(IsOrderFinalized);
salesOrderDetail.ReadOnly(d => d.Quantity).If(IsOrderFinalized);
salesOrderDetail.ReadOnly(d => d.UnitPrice).If(IsOrderFinalized);
}
static bool IsOrderFinalized(SalesOrder o) =>
o.Status == SalesOrderStatus.Shipped || o.Status == SalesOrderStatus.Closed;
static bool IsOrderFinalized(SalesOrderDetail d) =>
d.SalesOrder != null
&& (d.SalesOrder.Status == SalesOrderStatus.Shipped
|| d.SalesOrder.Status == SalesOrderStatus.Closed);
}
2. Multiple read-only conditions on the same property
A property can have multiple read-only rules. If any condition is true, the property is read-only.
[Logic]
public class SalesOrderDetailBL(SalesOrderDetail.Logic salesOrderDetail)
{
[RegisterLogic]
public void ReadOnlyOnOrderStatus()
{
salesOrderDetail.Quantity.ReadOnly()
.If(d => d.SalesOrder != null && d.SalesOrder.Status == SalesOrderStatus.Closed);
}
[RegisterLogic]
public void ReadOnlyOnDetailShipped()
{
// Also read-only when the detail line has been shipped
salesOrderDetail.Quantity.ReadOnly()
.If(d => d.IsShipped);
}
}
3. Checking read-only state before setting a property
When business logic needs to set a property that might be read-only, check the state first.
var isReadOnly = orderDetail._State.Quantity.IsReadOnly;
if (!isReadOnly)
orderDetail.Quantity = newQuantity;
Performance
Read-only conditions are evaluated on every property set and when the API retrieves property metadata. Your condition must be extremely fast:
- Do: Reference in-memory properties on the entity or its parent.
- Do not: Make database calls, LINQ queries, or expensive service calls. If you need data from the database or a service, compute it once into a property and reference that property in the read-only condition. This will only compute when necessary.
Notes
- Attempting to set a read-only property throws
InvalidOperationException. - The read-only state is tracked on
_State.<Property>.IsReadOnlyand is available to the API so the UI can disable fields. - Read-only conditions are cumulative — multiple subscribers combine with OR logic.