Skip to content

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.

Use Count() to increment a counter each time an event is processed:

public class UserActivityProjection : IProjectionFor<UserActivity>
{
public void Define(IProjectionBuilderFor<UserActivity> builder) => builder
.AutoMap()
.From<UserLoggedIn>(_ => _
.Count(m => m.LoginCount))
.From<UserPerformedAction>(_ => _
.Count(m => m.ActionCount));
}

The read model contains numeric properties for the counters:

public record UserActivity(
string Username,
int LoginCount,
int ActionCount);

Use Increment() and Decrement() to add or subtract 1 from a property:

public class InventoryProjection : IProjectionFor<Inventory>
{
public void Define(IProjectionBuilderFor<Inventory> builder) => builder
.AutoMap()
.From<ItemAdded>(_ => _
.Increment(m => m.Quantity))
.From<ItemRemoved>(_ => _
.Decrement(m => m.Quantity));
}

These functions always change the value by exactly 1.

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
.AutoMap()
.From<AccountOpened>(_ => _
.Set(m => m.Balance).ToValue(0m))
.From<MoneyDeposited>(_ => _
.Add(m => m.Balance).With(e => e.Amount))
.From<MoneyWithdrawn>(_ => _
.Subtract(m => m.Balance).With(e => e.Amount));
}

All projection functions work with these numeric types:

  • int
  • long
  • float
  • double
  • decimal

The functions automatically handle type conversion and maintain the target property’s type.

[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);
  1. Initialization: Properties start at 0 (or their default value) when first accessed
  2. Accumulation: Functions apply their operations incrementally as events are processed
  3. Type safety: Values are converted to match the target property type
  4. State preservation: Current values are maintained between events

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.