A provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10. Ships the model only — entities, conventions, and module configurations. You stay in control of the database provider and the Discord gateway.
Discord ids are 64-bit ulong snowflakes; relational providers store signed long.
Persistord handles the bit-faithful ulong ↔ long round-trip globally, models the
core Discord graph (guilds, channels, users, members, roles, messages), and adds
opt-in soft-delete and append-only history — without coupling you to a specific
database or Discord library.
| Package | Downloads | Adds | Depends on |
|---|---|---|---|
Persistord |
meta package — bundles Core, Messages, and History | Core, Messages, History | |
Persistord.Core |
snowflake conversion, base DiscordDbContext, core skeleton entities |
— | |
Persistord.Messages |
MessageEntity (soft-delete), embeds, attachments, reactions |
Core | |
Persistord.History |
append-only MessageHistoryEntity with a real FK to messages |
Messages | |
Persistord.Adapters.DiscordNet |
.To*Entity() mappers from Discord.Net types |
Core, Messages, History |
The core packages are independent of any Discord client library. Install the DiscordNet adapter only if you use Discord.Net.
# Recommended: the full library-neutral stack in one package
dotnet add package Persistord
# Or install modules individually:
dotnet add package Persistord.Core
dotnet add package Persistord.Messages # optional: message persistence
dotnet add package Persistord.History # optional: requires Messages
dotnet add package Persistord.Adapters.DiscordNet # optional: Discord.Net mappersInherit DiscordDbContext, expose the module DbSets you want, and apply the
module configurations in OnModelCreating:
using Microsoft.EntityFrameworkCore;
using Persistord.Core;
using Persistord.History;
using Persistord.History.Entities;
using Persistord.Messages;
using Persistord.Messages.Entities;
public sealed class MyBotContext : DiscordDbContext
{
public MyBotContext(DbContextOptions<MyBotContext> options) : base(options) { }
public DbSet<MessageEntity> Messages => Set<MessageEntity>();
public DbSet<MessageHistoryEntity> MessageHistory => Set<MessageHistoryEntity>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); // core skeleton + snowflake convention
modelBuilder.ApplyMessagesModule(); // omit if you don't persist messages
modelBuilder.ApplyHistoryModule(); // requires ApplyMessagesModule()
}
}Core entities (Guilds, Channels, Users, Members, Roles) are already
exposed by the base context — you only declare the module DbSets.
The consumer owns the provider choice. Any EF Core 10 relational provider works (PostgreSQL, SQL Server, SQLite, …); the snowflake conversion is applied automatically.
services.AddDbContextFactory<MyBotContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Bot")));A bot is long-lived and concurrent; a DbContext is neither thread-safe nor meant
to live forever. Create one per unit of work via the factory and dispose it:
await using var db = await factory.CreateDbContextAsync();
db.Messages.Add(new MessageEntity
{
Id = message.Id,
ChannelId = message.ChannelId,
AuthorId = message.Author.Id,
Content = message.Content,
});
await db.SaveChangesAsync();If you use Discord.Net, the adapter maps gateway/REST types for you:
using Persistord.Adapters.DiscordNet;
using Persistord.History.Entities;
db.Messages.Add(socketMessage.ToMessageEntity()); // embeds, attachments, reactions included
db.MessageHistory.Add(socketMessage.ToHistoryEntity(HistoryChangeType.Created));
await db.SaveChangesAsync();Deleting a message sets IsDeleted / DeletedAt rather than removing the row, and a
default query filter hides soft-deleted messages (IgnoreQueryFilters() to include
them, or ApplyMessagesModule(filterDeleted: false) to disable). Because the row
survives, MessageHistoryEntity's foreign key to it — including the row logging the
deletion — stays valid.
Full guides and the API reference live on the documentation site (built with DocFX). Start with Getting Started, browse the Guides and Recipes, or check Troubleshooting if something isn't working.
- Per-package READMEs: Core, Messages, History, Adapters.DiscordNet.
- Samples — runnable, focused walkthroughs (all SQLite):
samples/.
dotnet restore
dotnet build
dotnet testRequires the .NET 10 SDK. Formatting is enforced with ReSharper
(dotnet jb cleanupcode Persistord.slnx --profile="ReformatAndReorder").
MIT © Clergue Valentin