Projection functions
Projections support several built-in functions for mathematical operations and counting. These functions allow you to perform calculations directly within projections without needing custom logic.
Counting events
Use Count() to increment a counter each time an event is processed:
public class UserActivityProjection : IProjectionFor<UserActivity>
{
public void Define(IProjectionBuilderFor<UserActivity> builder) => builder
.From<UserLoggedIn>(_ => _
.Set(m => m.Username).To(e => e.Username)
.Count(m => m.LoginCount))
.From<UserPerformedAction>(_ => _
.Count(m => m.ActionCount));
}
Read model
The read model contains numeric properties for the counters:
public record UserActivity(
string Username,
int LoginCount,
int ActionCount);
Increment and decrement
Use Increment() and Decrement() to add or subtract 1 from a property:
public class InventoryProjection : IProjectionFor<Inventory>
{
public void Define(IProjectionBuilderFor<Inventory> builder) => builder
.From<ItemAdded>(_ => _
.Set(m => m.ItemName).To(e => e.Name)
.Increment(m => m.Quantity))
.From<ItemRemoved>(_ => _
.Decrement(m => m.Quantity));
}
These functions always change the value by exactly 1.
Add and subtract with values
Use Add() and Subtract() to add or subtract specific values from event properties:
public class AccountProjection : IProjectionFor<Account>
{
public void Define(IProjectionBuilderFor<Account> builder) => builder
.From<AccountOpened>(_ => _
.Set(m => m.AccountNumber).To(e => e.Number)
.Set(m => m.Balance).To(0m))
.From<MoneyDeposited>(_ => _
.Add(m => m.Balance).With(e => e.Amount))
.From<MoneyWithdrawn>(_ => _
.Subtract(m => m.Balance).With(e => e.Amount));
}
Supported types
All projection functions work with these numeric types:
intlongfloatdoubledecimal
The functions automatically handle type conversion and maintain the target property's type.
Event definitions
[EventType]
public record UserLoggedIn(string Username);
[EventType]
public record UserPerformedAction(string Username, string ActionType);
[EventType]
public record ItemAdded(string Name);
[EventType]
public record ItemRemoved(string Name);
[EventType]
public record AccountOpened(string Number);
[EventType]
public record MoneyDeposited(decimal Amount);
[EventType]
public record MoneyWithdrawn(decimal Amount);
How functions work
- Initialization: Properties start at 0 (or their default value) when first accessed
- Accumulation: Functions apply their operations incrementally as events are processed
- Type safety: Values are converted to match the target property type
- State preservation: Current values are maintained between events
Combining functions
You can use multiple functions in a single projection:
.From<Transaction>(_ => _
.Count(m => m.TransactionCount)
.Add(m => m.TotalAmount).With(e => e.Amount)
.Increment(m => m.ProcessedEvents))
These functions provide powerful aggregation capabilities while keeping projection logic simple and declarative.