Summary#
What one commit raises is delivered together — a ticket arriving, being queued, being assigned is ONE delivery. A
handler of every event that reads its rules for each event would read them three times; marked [OncePerDelivery],
the read is made once and every event's handler is handed that answer:
[OncePerDelivery]
List<Rule> SavedRules(string subject) { return Rule.Where(r => r.Subject == subject).ToList(); }Signature#
[OncePerDelivery]
<Type> <Name>(<params>) { … } // answers something; called by a handler, or by what a handler callsDescription#
What is remembered?#
The answer, per set of arguments — strings, numbers, yes-or-no, Guids, dates and records (by id). A call with any other kind of argument is not remembered: it simply runs. A list it answers is handed to each caller as their own copy, so a handler adding to it does not change what the next event's handler gets; the records in it are the same records, read on the delivery's own unit of work.
For how long?#
For one delivery — the events of one commit (or of one workflow step) delivered together. The next delivery reads again. So the function should READ what the delivery itself does not change: rules, subscriptions, settings. What a handler writes during the delivery is not in an answer remembered before it.
Outside a delivery?#
An ordinary call: a page, a test or the hourly clock calling the same function reads every time.
What is refused?#
A function that answers nothing, a handler, an action or a test, and a method of a class:
`[OncePerDelivery]` remembers what a function ANSWERS for the rest of a delivery — 'RemembersNothing' answers nothing, so there is nothing to remember. Put it on the function that reads: `[OncePerDelivery] List<Rule> SavedRules() { … }`.Examples#
Three events, one read#
One commit raises three events; each handler run reads the notes through Notes(), which is read once between them.
entity Lamp {
[MaxLength(40)] string Name = "";
security { allow read, create, update when IsAnonymous || IsAuthenticated; }
}
entity Note {
[MaxLength(100)] string Text = "";
security { allow read, create when IsAnonymous || IsAuthenticated; }
}
entity Heard {
[MaxLength(200)] string Says = "";
security { allow read, create when IsAnonymous || IsAuthenticated; }
}
[Event] public sealed class LampLit { public Lamp Lamp; }
[On]
void Remember(Event e) { new Heard { Says = Events.Type(e) + " with " + Notes().Count + " notes" }; }
[OncePerDelivery]
List<Note> Notes() { return Note.ToList(); }
void LightThrice(Lamp l) {
Raise(new LampLit { Lamp = l });
Raise(new LampLit { Lamp = l });
Raise(new LampLit { Lamp = l });
}[Test]
void Every_event_of_the_delivery_reads_the_same_notes() {
new Note { Text = "bright" };
var lamp = new Lamp { Name = "Porch" };
Background.Settle();
LightThrice(lamp);
Background.Settle();
Assert.Equal(3, Heard.Count(h => h.Says == "LampLit with 1 notes"));
}See also#
- [On] — hear an event, after the commit that raised it — hearing an event, and when events are delivered together
- [Interest] — the events a handler of every event cares about — a handler of every event says which it cares about
- Raise(e) — say that something happened — what raising an event costs