Multi-Subscriber Compute
Summary
You can attach many compute subscribers to one property. Each later subscriber can read the prior subscriber's value, modify it, or fully replace it.
Subscribers run bottom-up (last registered first). A subscriber only triggers earlier ones when it reads args.PriorSubscriberValue.
How it runs
graph BT
S1["S1 — base price = 30"]
S2["S2 — adds 10 to prior"]
S3["S3 — If contract → 45"]
S3 -->|"If false<br/>→ skip to S2"| S2
S2 -->|"reads<br/>PriorSubscriberValue"| S1
- Start at the last subscriber.
- If its
Ifis false → move down to the next subscriber. - If its expression reads
args.PriorSubscriberValue→ the next-lower subscriber runs. - If its expression does not read
args.PriorSubscriberValue→ the lower subscribers are skipped.
Three patterns
1. Base value
invoice.Compute(d => d.UnitPrice)
.From(d => 30m)
.DirtyBy(d => d.Product);
2. Modify prior value
invoice.Compute(d => d.UnitPrice)
.From((d, args) => args.PriorSubscriberValue + 10m)
.DirtyBy(d => d.Product);
3. Override with skip
invoice.Compute(d => d.UnitPrice)
.If(d => d.Doc.SellToCustomer.HasContractPricing())
.From(d => 45m) // does not read PriorSubscriberValue
.DirtyBy(d => d.Product);
When If is true → S1 and S2 never run.
When If is false → engine falls through to S2 → S2 reads prior → S1 runs.
Result table
| Scenario | S3 If | Final value | Subscribers that ran |
|---|---|---|---|
| Contract customer | true | 45 | S3 |
| Regular customer | false | 40 | S3 (skipped) → S2 → S1 |
| Only S1 registered | — | 30 | S1 |
| Only S2 registered | — | 10 | S2 (prior = default(decimal) = 0) |
PriorSubscriberValue
public TValue? PriorSubscriberValue { get; }
- Lazy — does not evaluate prior subscribers unless you read it.
- Cached — reading it twice in one subscriber runs prior only once.
- Default — returns
default(TValue)if there is no prior subscriber.
Multiple subscribers across [Logic] classes
Subscribers from different [Logic] classes can target the same property. Order is decided by feature ordering (see Features) at generator time, then by [RegisterLogic] method source order within one class.
graph LR
A["Sales/SalesPriceBL<br/>.From(c => 100)"] --> B["_computeEvents"]
C["Sales/Doc/SalesDocPriceBL<br/>.From((c,a) => a.PriorSubscriberValue + 50)"] --> B
B --> D["Read entity.Price → 150"]