Sample DbContext (EF Core)
Default Tables
Some features rely on data being stored in a persistent storage such as a database. This chapter highlights the DbSet
's that have to be added to your DbContext
when using Silverback in combination with EF Core (via the Silverback.Core.EntityFrameworkCore).
Here a breakdown of the use cases that require a DbSet
:
- Using an outbox table (see Outbound Endpoint) will require a
DbSet<OutboundMessage>
and possibly aDbSet<Lock>
, to enable horizontal scaling. - Either a
DbSet<StoredOffset>
or aDbSet<InboundMessage>
is necessary to ensure exactly-once processing (see Inbound Endpoint). - When consuming chunked messages (see Chunking), you may want to temporary store the received chunks into a database table, until all chunks are received and the full message can be rebuilt and processed and you therefore need a
DbSet<TemporaryMessageChunk>
to be configured.
This is what a DbContext
built to support all the aforementioned features will look like.
using Microsoft.EntityFrameworkCore;
using Silverback.Database.Model;
using Silverback.EntityFrameworkCore;
namespace Sample
{
public class SampleDbContext : DbContext
{
public SampleDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<OutboxMessage> Outbox { get; set; } = null!;
public DbSet<InboundLogEntry> InboundMessages { get; set; } = null!;
public DbSet<StoredOffset> StoredOffsets { get; set; } = null!;
public DbSet<Lock> Locks { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<InboundLogEntry>()
.HasKey(t => new { t.MessageId, t.ConsumerGroupName });
}
}
}
Warning
InboundLogEntry declare a composite primary key via annotation, thing that isn't supported yet by EF Core. It is therefore mandatory to explicitly redeclare their primary key via the HasKey
fluent API.
DDD and Transactional Messages
Some additional changes are required in order for the events generated by the domain entities to be fired as part of the SaveChanges
transaction. More details on this topic can be found in the DDD and Domain Events section.
using Microsoft.EntityFrameworkCore;
using Silverback.EntityFrameworkCore;
using Silverback.Messaging.Publishing;
namespace Sample
{
public class SampleDbContext : DbContext
{
private readonly DbContextEventsPublisher _eventsPublisher;
public SampleDbContext(IPublisher publisher)
{
_eventsPublisher = new DbContextEventsPublisher(publisher, this);
}
public SampleDbContext(DbContextOptions options, IPublisher publisher)
: base(options)
{
_eventsPublisher = new DbContextEventsPublisher(publisher, this);
}
// ...DbSet properties and OnModelCreating...
public override int SaveChanges()
=> SaveChanges(true);
public override int SaveChanges(bool acceptAllChangesOnSuccess)
=> _eventsPublisher.ExecuteSaveTransaction(() => base.SaveChanges(acceptAllChangesOnSuccess));
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
=> SaveChangesAsync(true, cancellationToken);
public override Task<int> SaveChangesAsync(
bool acceptAllChangesOnSuccess,
CancellationToken cancellationToken = default)
=> _eventsPublisher.ExecuteSaveTransactionAsync(() =>
base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken));
}
}