Recipes
Copy-pasteable patterns for the most common Persistord operations. Each snippet
assumes you already have a short-lived DbContext obtained from
IDbContextFactory — see DbContext Lifetime.
Persist a guild
Add a GuildEntity and save. Id, Name, and OwnerId are the three required
fields.
db.Guilds.Add(new GuildEntity { Id = guildId, Name = name, OwnerId = ownerId });
await db.SaveChangesAsync();
Log a message create, edit, and delete
Add the MessageEntity row once, then append a MessageHistoryEntity for each
change. Each history row is a full content snapshot tagged with a HistoryChangeType.
// On message create: add the message and log a Created snapshot.
db.Messages.Add(new MessageEntity
{
Id = messageId,
ChannelId = channelId,
AuthorId = authorId,
Content = content,
});
db.MessageHistory.Add(new MessageHistoryEntity
{
MessageId = messageId,
Content = content,
RecordedAt = DateTimeOffset.UtcNow,
ChangeType = HistoryChangeType.Created,
});
await db.SaveChangesAsync();
// On message edit: update the message and append an Edited snapshot.
// message.Content = newContent; message.EditedAt = DateTimeOffset.UtcNow;
// db.MessageHistory.Add(new MessageHistoryEntity { ..., ChangeType = HistoryChangeType.Edited });
// On message delete: soft-delete the message (set IsDeleted/DeletedAt) and append a Deleted snapshot.
// message.IsDeleted = true; message.DeletedAt = DateTimeOffset.UtcNow;
// db.MessageHistory.Add(new MessageHistoryEntity { ..., ChangeType = HistoryChangeType.Deleted });
Read soft-deleted messages
The default query filter hides soft-deleted messages. Call IgnoreQueryFilters()
to bypass it for a single query.
var all = db.Messages.IgnoreQueryFilters().ToList();
Query a message's history chronologically
History rows are indexed on (MessageId, RecordedAt). Order by RecordedAt on
providers that support DateTimeOffset ordering (PostgreSQL, SQL Server); order by
the surrogate Id on SQLite.
var history = db.MessageHistory
.Where(h => h.MessageId == id)
.OrderBy(h => h.RecordedAt)
.ToList();
Swap the database provider
The model is provider-agnostic. Change only the provider call in your DI
registration; the snowflake ulong ↔ long conversion applies automatically
regardless of provider.
// PostgreSQL
options.UseNpgsql(connectionString);
// SQLite
options.UseSqlite(connectionString);
// SQL Server
options.UseSqlServer(connectionString);
Map from Discord.Net
Install Persistord.Adapters.DiscordNet and call .ToMessageEntity() directly on
the Discord.Net object. See Discord.Net Adapter for the
full mapper table.
db.Messages.Add(socketMessage.ToMessageEntity());
await db.SaveChangesAsync();