Table of Contents

Introduction

Persistord is a provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10.

What it is

Persistord ships the model only: entities, conventions, and module configurations. It never selects a database provider, never talks to Discord, and never references a Discord client library. You compose it into your own bot and stay in control of the provider and the gateway.

The library defines the data shape of common Discord entities and the plumbing — a base DbContext, snowflake value conversions, and opt-in modules — while leaving you in full control of what gets persisted, when, and with which database provider.

The promise

The guiding promise is "persist whatever you choose to" — not state replication or gateway sync. Persistord gives you a clean, reliable relational model to write into; it never mirrors live state automatically.

Why it exists

Discord ids are 64-bit ulong snowflakes; relational providers store signed long. Persistord handles the bit-faithful ulong ↔ long round-trip globally via a single convention registered in ConfigureConventions, so you never annotate individual id properties. See snowflake conversion for details.

Beyond that, the library models the core Discord graph — guilds, channels, users, members, roles, and messages — as plain POCOs, plus opt-in soft-delete and append-only history.

What it is not

The following are explicit non-goals in v1:

  • No gateway event handling, no automatic sync, no reconnect backfill, no reconciliation.
  • No general conflict-resolution engine. Persistord.Core does ship a narrow natural-key UpsertAsync/UpsertIfChangedAsync (create-or-update with lost-insert-race recovery) for rows the bot owns — that is deliberately not an engine, just the one write pattern the "persist what you choose" model needs.
  • No caching layer.
  • No diff-based history (full content snapshot per change in v1).

Mapping from Discord.Net / DSharpPlus / NetCord model types to Persistord entities is the user's responsibility — though the optional Persistord.Adapters.DiscordNet, Persistord.Adapters.DSharpPlus and Persistord.Adapters.NetCord packages provide ready-made mappers for all three libraries.

Packages

Persistord is split into ten NuGet packages:

Persistord — the convenience meta package. Installing it pulls in the full library-neutral stack (Core, Messages, and History) in one reference. This is the recommended starting point.

Persistord.Core — the foundation: snowflake conversion, the conventions-only DiscordDbContext base class, and the abstract DiscordGraphDbContext that adds the opt-in core skeleton entities (GuildEntity, ChannelEntity, UserEntity, MemberEntity, RoleEntity) for a bot that mirrors Discord's guild/channel/user/member/role graph rather than owning its own resources.

Persistord.Messages — the optional message-persistence module. Adds MessageEntity (with soft-delete), owned embeds, and relational attachments and reactions, wired in via ApplyMessagesModule(). Depends on Persistord.Core.

Persistord.History — the optional append-only history module. Adds MessageHistoryEntity with a real foreign key to MessageEntity, wired in via ApplyHistoryModule(). Depends on Persistord.Messages.

Persistord.Adapters.DiscordNet — an optional adapter that maps Discord.Net interface types (IGuild, IMessage, etc.) to Persistord entities via .To*Entity() extension methods. Install only if you use Discord.Net; the core packages never reference a Discord client library.

Persistord.Adapters.DSharpPlus — an optional adapter that maps DSharpPlus model types (DiscordGuild, DiscordMessage, etc.) to Persistord entities via .To*Entity() extension methods. Install only if you use DSharpPlus; the core packages never reference a Discord client library. ToMemberEntity and ToRoleEntity take the guild id as an argument, because DSharpPlus does not expose it on those two types.

Persistord.Adapters.NetCord — an optional adapter that maps NetCord model types (RestGuild, RestMessage, etc.) to Persistord entities via .To*Entity() extension methods. Install only if you use NetCord; the core packages never reference a Discord client library.

Persistord.Managed — records of the categories, channels, anchored messages, and webhooks a bot creates and owns, keyed by a name you chose. See Managed Resources. Depends on Persistord.Core.

Persistord.Protection — encrypts [Protected] string columns of a context at rest via ASP.NET Core Data Protection. See Protection. Depends on Persistord.Core.

Persistord.Testing — in-memory SQLite fixtures and EF Core model assertions for testing a Persistord-based context. See Testing. Depends on Persistord.Core.

Persistord.Managed, Persistord.Protection, and Persistord.Testing are opt-in and not part of the Persistord meta package: a bot that only mirrors Discord never owns resources, encrypts a column, or needs the test fixtures, so the meta package stays the library-neutral mirror stack (Core, Messages, History) and nothing more.

The dependency graph is not linear: Messages depends on Core, History depends on Messages, and all three of Adapters.DiscordNet, Adapters.DSharpPlus and Adapters.NetCord depend on all three — that chain is the one the meta package bundles. Managed, Protection, and Testing each depend on Core alone, independently of that chain and of each other.

To get started, see Getting Started.