The Entity Framework Core store provider offers a variety of built-in extension methods of the DbContext to facilitate interaction with aggregates and events. Since the store provider is based purely on the DbContext, it’s extremily easy to create your own extensions to create any kind of reporting. Below is a categorized list of the built-in methods:
Saves an aggregate to the event store with optimistic concurrency control, persisting all uncommitted domain events and updating the aggregate snapshot.
New aggregate
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregate = new OrderAggregate(orderId, amount: 25.45m);
var saveAggregateResult = await dbContext.SaveAggregate(streamId, aggregateId, aggregate, expectedEventSequence: 0);
Update existing aggregate
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var latestEventSequence = await domainDbContext.GetLatestEventSequence(streamId);
var aggregateResult = await dbContext.GetAggregate(streamId, aggregateId);
if (!aggregateResult.IsSuccess)
{
return aggregateResult.Error;
}
aggregate = aggregateResult.Value;
aggregate.UpdateAmount(amount: 15.00m);
var saveAggregateResult = await dbContext.SaveAggregate(streamId, aggregateId, aggregate, expectedEventSequence: latestEventSequence);
Saves an array of domain events to the event store with optimistic concurrency control, bypassing aggregate persistence. This method is ideal for scenarios where events are generated outside traditional aggregate workflows.
var streamId = new CustomerStreamId(customerId);
var latestEventSequence = await domainDbContext.GetLatestEventSequence(streamId);
var events = new @event[]
{
new OrderPlaced
{
OrderId = orderId,
Amount = 25.45m
},
new OrderShipped
{
OrderId = orderId,
ShippedDate = _timeProvider.GetUtcNow()
}
};
var saveEventsResult = await dbContext.SaveEvents(streamId, events, expectedEventSequence: latestEventSequence);
Saves all pending changes in the domain database context to the underlying data store. This method provides a simple way to persist tracked entity changes without additional event sourcing logic, suitable for scenarios where entities have been explicitly tracked.
// ...track aggregates and domain events...
var item = new ItemEntity
{
Id = Guid.NewGuid(),
Name = "Sample Item",
Price = 9.99m
};
dbContext.Items.Add(item);
var saveResult = await dbContext.Save();
Updates an existing aggregate with new events from its stream, applying any events that occurred after the aggregate’s last known state.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var updateAggregateResult = await dbContext.UpdateAggregate(streamId, aggregateId);
Tracks an aggregate’s uncommitted events and state changes in the Entity Framework change tracker without persisting to the database, preparing all necessary entities for subsequent save operations.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var latestEventSequence = await domainDbContext.GetLatestEventSequence(streamId);
var aggregateResult = await dbContext.GetAggregate(streamId, aggregateId);
if (!aggregateResult.IsSuccess)
{
return aggregateResult.Error;
}
aggregate = aggregateResult.Value;
aggregate.UpdateAmount(amount: 15.00m);
await dbContext.TrackAggregate(streamId, aggregateId, aggregate, expectedEventSequence: latestEventSequence);
// ...additional entity changes...
var saveResult = await dbContext.Save();
Tracks an array of domain events in the Entity Framework change tracker without persisting to the database, preparing event entities for later save operations with proper sequencing and concurrency control validation.
var streamId = new CustomerStreamId(customerId);
var latestEventSequence = await domainDbContext.GetLatestEventSequence(streamId);
var events = new @event[]
{
new OrderPlaced
{
OrderId = orderId,
Amount = 25.45m
},
new OrderShipped
{
OrderId = orderId,
ShippedDate = _timeProvider.GetUtcNow()
}
};
await dbContext.TrackEvents(streamId, events, expectedEventSequence: latestEventSequence);
// ...additional entity changes...
var saveResult = await dbContext.Save();
Tracks an aggregate’s state changes based on a list of event entities, applying only events that the aggregate can handle and updating its snapshot accordingly.
var streamId = new CustomerStreamId(customerId);
var orderAggregateId = new OrderAggregateId(orderId);
var anotherAggregateId = new AnotherAggregateId(orderId);
var aggregate = new OrderAggregate(orderId, amount: 25.45m);
var trackAggregateResult = await dbContext.TrackAggregate(streamId, orderAggregateId, aggregate, expectedEventSequence: 0);
if (!trackAggregateResult.IsSuccess)
{
return trackResult.Error;
}
// Track same event entities for a different aggregate
await dbContext.TrackEventEntities(streamId, anotherAggregateId, trackAggregateResult.Value.EventEntities!, expectedEventSequence: 0);
var saveResult = await dbContext.Save();
Retrieves an aggregate from the event store, either from its snapshot or by reconstructing it from events.
If the aggregate does not exist, but domain events that can be applied to the aggregate exist, the aggregate snapshot is stored automatically. This is useful when the domain changes, and you need a different aggregate structure. Increase the version of the aggregate type to force a snapshot creation.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await dbContext.GetAggregate(streamId, aggregateId);
Optionally, it can be forced to apply any new domain events that occurred after the snapshot was created. This is useful when you want to ensure the aggregate is up to date with the latest events. If new events are found, the aggregate snapshot is updated automatically.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await dbContext.GetAggregate(streamId, aggregateId, applyNewEvents: true);
Reconstructs an aggregate entirely from events without using snapshots, providing a pure event-sourced view of the aggregate state.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await dbContext.GetInMemoryAggregate(streamId, aggregateId);
Retrieves all domain events from a specified stream, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var eventsResult = await dbContext.GetEvents(streamId);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEvents(streamId, eventTypes);
Retrieves domain events from a specified stream starting from a specific sequence number onwards, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventsResult = await dbContext.GetEventsFromSequence(streamId, fromSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsFromSequence(streamId, fromSequence, eventTypes);
Retrieves domain events from a specified stream up to and including a specific sequence number, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventsResult = await dbContext.GetEventsUpToSequence(streamId, upToSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsUpToSequence(streamId, upToSequence, eventTypes);
Retrieves domain events from a specified stream from and to specific sequence numbers, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventsResult = await dbContext.GetEventsBetweenSequences(streamId, fromSequence, toSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsBetweenSequences(streamId, fromSequence, toSequence, eventTypes);
Retrieves domain events from a specified stream starting from a specific date onwards, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventsResult = await dbContext.GetEventsFromDate(streamId, fromDate);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsFromDate(streamId, fromDate, eventTypes);
Retrieves domain events from a specified stream up to and including a specific date, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var upToDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventsResult = await dbContext.GetEventsUpToDate(streamId, upToDate);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var upToDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsUpToDate(streamId, upToDate, eventTypes);
Retrieves domain events from a specified stream from and to specific dates, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var toDate = new DateTime(2024, 6, 25, 12, 46, 22);
var eventsResult = await dbContext.GetEventsBetweenDates(streamId, fromDate, toDate);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var toDate = new DateTime(2024, 6, 25, 12, 46, 22);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.GetEventsBetweenDates(streamId, fromDate, toDate, eventTypes);
Retrieves all domain events that have been applied to a specific aggregate instance, using the explicit aggregate-event relationship tracking. This method provides precise access to the events that actually contributed to an aggregate’s current state.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var eventsResult = await dbContext.GetEventsAppliedToAggregate(streamId, aggregateId);
Retrieves the latest event sequence number for a specified stream, with optional filtering by event types. This method provides the current position in an event stream, essential for optimistic concurrency control and determining where to append new events in event sourcing operations.
var streamId = new CustomerStreamId(customerId);
var latestEventSequence = await dbContext.GetLatestEventSequence(streamId);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var latestEventSequence = await dbContext.GetLatestEventSequence(streamId, eventTypes);
Retrieves all event entities from a specified stream, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var eventEntitiesResult = await dbContext.GetEventEntities(streamId);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventEntitiesResult = await dbContext.GetEventEntities(streamId, eventTypes);
Retrieves event entities from a specified stream from and to specific sequence numbers, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventsResult = await dbContext.EventEntitiesBetweenSequences(streamId, fromSequence, toSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await dbContext.EventEntitiesBetweenSequences(streamId, fromSequence, toSequence, eventTypes);
Retrieves a list of event entities from the specified stream starting from a given sequence number, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventEntitiesResult = await dbContext.GetEventEntitiesFromSequence(streamId, fromSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventEntitiesResult = await dbContext.GetEventEntitiesFromSequence(streamId, fromSequence, eventTypes);
Retrieves event entities from a specified stream up to and including a specific sequence number, with optional filtering by event types.
var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventEntitiesResult = await dbContext.GetEventEntitiesUpToSequence(streamId, upToSequence);
Optionally, you can filter the events by specific event types.
var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventEntitiesResult = await dbContext.GetEventEntitiesUpToSequence(streamId, upToSequence, eventTypes);
Retrieves all event entities that have been applied to a specific aggregate instance, providing a complete audit trail of changes that contributed to the aggregate’s current state.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var eventEntitiesResult = await dbContext.GetEventEntitiesAppliedToAggregate(streamId, aggregateId);
Retrieves all aggregate-event relationship entities associated with a specific aggregate instance, providing complete visibility into the many-to-many relationships between the aggregate and its applied events.
var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateEventEntitiesResult = await dbContext.GetAggregateEventEntities(streamId, aggregateId);