Getting a Collection of Instances
The IReadModels API provides the GetInstances<TReadModel>() method to retrieve all instances of a read model by replaying all events from the event log. This is useful for querying, reporting, and analysis across your entire read model dataset.
Overview
Section titled “Overview”When you request all instances of a read model using GetInstances<T>, Chronicle:
- Identifies the read model type and its backing projection or reducer
- Retrieves all events from the event log
- Applies the projection or reducer logic to build all instances
- Returns a collection of the resulting read model instances
This method scans the complete event history and produces a snapshot of all read model instances as they currently exist.
Basic Usage
Section titled “Basic Usage”Getting All Instances
Section titled “Getting All Instances”Retrieve all instances of a read model type:
public class ReportingService{ readonly IEventStore _eventStore;
public ReportingService(IEventStore eventStore) { _eventStore = eventStore; }
public async Task<IEnumerable<Account>> GetAllAccounts() { return await _eventStore.ReadModels.GetInstances<Account>(); }}Filtering Results
Section titled “Filtering Results”The GetInstances method returns all instances, which you can then filter using LINQ:
public async Task<IEnumerable<Account>> GetHighValueAccounts(decimal threshold){ var allAccounts = await _eventStore.ReadModels.GetInstances<Account>();
return allAccounts .Where(a => a.Balance > threshold) .OrderByDescending(a => a.Balance) .ToList();}Limiting Event Processing
Section titled “Limiting Event Processing”For performance optimization, you can limit the number of events processed:
public async Task<IEnumerable<Order>> GetOrdersLimitedToLastEvents(){ // Only process the last 1000 events return await _eventStore.ReadModels.GetInstances<Order>(1000);}Working with Different Read Model Types
Section titled “Working with Different Read Model Types”Projection-Based Read Models
Section titled “Projection-Based Read Models”For read models defined using projections:
// Model-bound projectionpublic record OrderSummary( [Key] Guid OrderId, [SetFrom<OrderCreated>(nameof(OrderCreated.CustomerId))] Guid CustomerId, [SetFrom<OrderCreated>(nameof(OrderCreated.TotalAmount))] [AddFrom<PaymentReceived>(nameof(PaymentReceived.Amount))] decimal TotalPaid);
// Retrieve all order summariespublic async Task<IEnumerable<OrderSummary>> GetAllOrderSummaries(){ return await _eventStore.ReadModels.GetInstances<OrderSummary>();}Reducer-Based Read Models
Section titled “Reducer-Based Read Models”For read models defined using reducers:
public record ShoppingCart(Guid Id, List<CartItem> Items, decimal Total);
public class ShoppingCartReducer : IReducerFor<ShoppingCart>{ public ShoppingCart OnCartCreated(CartCreated @event, ShoppingCart? current, EventContext context) => (current ?? new ShoppingCart(Guid.Empty, [], 0m)) with { Id = @event.CartId };
public ShoppingCart OnItemAdded(ItemAdded @event, ShoppingCart? current, EventContext context) { current ??= new ShoppingCart(Guid.Empty, [], 0m); return current with { Items = [.. current.Items, new CartItem(@event.ProductId, @event.Quantity, @event.Price)], Total = current.Total + (@event.Quantity * @event.Price) }; }}
// Retrieve all shopping cartspublic async Task<IEnumerable<ShoppingCart>> GetAllActiveCarts(){ var carts = await _eventStore.ReadModels.GetInstances<ShoppingCart>(); return carts.Where(c => c.Items.Any()).ToList();}Common Queries
Section titled “Common Queries”Getting Statistics
Section titled “Getting Statistics”Compute aggregates across all instances:
public async Task PrintAccountStatistics(){ var accounts = await _eventStore.ReadModels.GetInstances<Account>();
var stats = new { TotalAccounts = accounts.Count(), AverageBalance = accounts.Average(a => a.Balance), TotalBalance = accounts.Sum(a => a.Balance), HighestBalance = accounts.Max(a => a.Balance), LowestBalance = accounts.Min(a => a.Balance) };
Console.WriteLine($"Total Accounts: {stats.TotalAccounts}"); Console.WriteLine($"Average Balance: {stats.AverageBalance:C}"); Console.WriteLine($"Total Balance: {stats.TotalBalance:C}");}Finding Specific Instances
Section titled “Finding Specific Instances”Search across all instances for specific conditions:
public async Task<User?> FindUserByEmail(string email){ var allUsers = await _eventStore.ReadModels.GetInstances<User>(); return allUsers.FirstOrDefault(u => u.Email == email);}
public async Task<IEnumerable<Order>> FindOrdersByStatus(OrderStatus status){ var allOrders = await _eventStore.ReadModels.GetInstances<Order>(); return allOrders.Where(o => o.Status == status).ToList();}Grouping and Aggregation
Section titled “Grouping and Aggregation”Group instances for reporting:
public async Task PrintTransactionsByType(){ var transactions = await _eventStore.ReadModels.GetInstances<Transaction>();
var grouped = transactions .GroupBy(t => t.Type) .Select(g => new { Type = g.Key, Count = g.Count(), TotalAmount = g.Sum(t => t.Amount) });
foreach (var group in grouped) { Console.WriteLine($"{group.Type}: {group.Count} transactions, Total: {group.TotalAmount:C}"); }}Performance Considerations
Section titled “Performance Considerations”Event History Impact
Section titled “Event History Impact”The performance of GetInstances depends on the total number of events in the event log:
- Small event logs (hundreds of events): Fast, typically under 500ms
- Medium event logs (thousands of events): Moderate, typically under 2 seconds
- Large event logs (tens of thousands of events): Slower, may take several seconds
Memory Usage
Section titled “Memory Usage”All instances are loaded into memory. For large datasets:
- Large number of instances: Consider pagination or filtering at the query level
- Large individual instances: Be aware of total memory consumption
- Streaming alternative: For very large datasets, consider using materialized projections stored in a database instead
When to Use GetInstances
Section titled “When to Use GetInstances”Use GetInstances when:
- You need to report on all instances
- You’re performing analysis across your entire dataset
- The dataset is reasonably small (hundreds to low thousands of instances)
Avoid GetInstances when:
- You only need a single instance (use
GetInstanceByIdinstead) - You have a very large number of read model instances
- You need filtering/pagination capabilities (use materialized projections instead)
Caching Strategies
Section titled “Caching Strategies”For reports that are generated infrequently, you can cache the entire result set:
public class CachedReportingService{ readonly IEventStore _eventStore; readonly IMemoryCache _cache;
public async Task<IEnumerable<Account>> GetAllAccountsCached() { const string cacheKey = "all-accounts";
if (_cache.TryGetValue<IEnumerable<Account>>(cacheKey, out var cached)) { return cached; }
var accounts = await _eventStore.ReadModels.GetInstances<Account>();
// Cache for 1 hour _cache.Set(cacheKey, accounts, TimeSpan.FromHours(1));
return accounts; }}Important: Always implement cache invalidation when events are appended that affect your read models.
Use Cases
Section titled “Use Cases”Reporting and Analytics
Section titled “Reporting and Analytics”Generate reports across all instances:
public async Task ExportAccountsReport(){ var accounts = await _eventStore.ReadModels.GetInstances<Account>();
var report = accounts .OrderByDescending(a => a.Balance) .Select(a => new { a.Id, a.Name, a.Balance, a.CreatedDate });
await ExportToCsv(report);}Bulk Operations
Section titled “Bulk Operations”Process all instances for administrative tasks:
public async Task RecalculateAllProjections(){ var orders = await _eventStore.ReadModels.GetInstances<Order>();
foreach (var order in orders) { await ProcessOrderForAnalytics(order); }}
### Time Travel and Point-in-Time Reads
Rebuild a read model collection up to a known point in time by pairing the event count limit with a captured sequence position from the event sequence state. This is useful for audits, back-testing, and historical reporting. See [Getting state](../events/getting-state) for how to capture the sequence position.Data Validation
Section titled “Data Validation”Verify data integrity across all instances:
public async Task ValidateDataIntegrity(){ var carts = await _eventStore.ReadModels.GetInstances<ShoppingCart>();
var invalidCarts = carts.Where(c => c.Items.Sum(i => i.Quantity) < 0).ToList();
if (invalidCarts.Any()) { foreach (var cart in invalidCarts) { Console.WriteLine($"Invalid cart found: {cart.Id}"); } }}Related Topics
Section titled “Related Topics”- Getting a Single Instance - Retrieve a specific read model instance by key
- Getting Snapshots - Retrieve historical state snapshots
- Watching Read Models - Real-time notifications for read model changes
- Projections - Learn more about defining projections
- Reducers - Learn more about defining reducers
- Events - Getting state - Capture sequence position for point-in-time reads