Relatude.DB.CMS 1.0.21

dotnet add package Relatude.DB.CMS --version 1.0.21
                    
NuGet\Install-Package Relatude.DB.CMS -Version 1.0.21
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Relatude.DB.CMS" Version="1.0.21" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Relatude.DB.CMS" Version="1.0.21" />
                    
Directory.Packages.props
<PackageReference Include="Relatude.DB.CMS" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Relatude.DB.CMS --version 1.0.21
                    
#r "nuget: Relatude.DB.CMS, 1.0.21"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Relatude.DB.CMS@1.0.21
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Relatude.DB.CMS&version=1.0.21
                    
Install as a Cake Addin
#tool nuget:?package=Relatude.DB.CMS&version=1.0.21
                    
Install as a Cake Tool

Relatude.CMS

A .NET 8 content backend layered on Relatude.DB, an in-process object-oriented graph database (NuGet Relatude.DB.Server). You model your domain as C# interfaces (the engine generates the implementing classes), declare relationships as small marker classes, and read/write through a NodeStore. Provides the base content model (IItem, IPage, ISite, ITemplate), dynamic URL routing, and DB access plumbing.

Using an AI coding agent? This package ships a Claude Code skill (relatude-cms) with the same guidance below. The commerce layer is documented in Relatude.Ecom (relatude-ecom skill), which builds on this one.

Host setup (Program.cs)

Relatude.DB runs in-process inside an ASP.NET Core app:

var builder = WebApplication.CreateBuilder(args);
builder.AddRelatudeDB();          // from Relatude.DB.Server, no namespace needed
builder.Services.AddHttpContextAccessor();
// register CMS services (routing, URL service, request context) here

var app = builder.Build();
app.UseRelatudeDB();              // starts the engine; admin UI at /relatude.db
app.Run();

AddRelatudeDB registers a DI type Database : NodeStore (transient) you can inject directly. The schema (datamodel) is managed through the admin UI at /relatude.db, where you point a datamodel source at the assembly/types holding your model interfaces.

Getting a NodeStore

NodeStore (namespace Relatude.DB.Nodes) is the entire data API. Obtain one by injecting Database, by injecting IRelatudeDBAccessor (Relatude.CMS.Engine) and using .Database, or statically via RelatudeDBRuntime.Server. Convention: methods that touch data take NodeStore db as a parameter rather than capturing it.

Modeling: interfaces, not classes

An entity is an interface extending IItem (and usually IPage for routable content):

public interface IPage : IItem {
    string MetaTitle { get; set; }
    string MetaDescription { get; set; }
    TemplatePages.Template Template { get; set; }   // relation end → the one Template for this page
}

IItem gives every node Guid Id, string Name, NodeMeta Meta (Meta.Address is the URL slug). IPage adds SEO metadata and a Template relation end. Other base types: ISite, ITemplate, ITree, ICountry, ICMSUser, ICmsUserGroup.

Relations are declared as classes deriving from OneToMany<,>, ManyToMany<,>, OneToOne<,>, OneOne<>, or ManyMany<>, with nested marker classes naming each end:

public class TemplatePages : OneToMany<ITemplate, IPage> {
    public class Template : One { }   // exposed on IPage
    public class Pages : Many { }     // exposed on ITemplate
}

OneToMany<TOne,TMany> exposes One/Many; ManyToMany<TFrom,TTo> exposes ManyFrom/ManyTo; OneToOne<,> exposes OneFrom/OneTo. Attributes (Relatude.DB.Nodes) tune mapping: [Node(...)] on the interface, [Exclude], and per-type property attributes ([StringProperty], [IntegerProperty(Indexed=true, UniqueValues=true)], [DecimalProperty], [GuidProperty], …). Embedded value maps use [EmbeddedMapProperty(KeyProperty = ...)].

Relation ends are objects, not plain collections:

if (page.Template.IsSet()) { … }          // One: has value? / Many: Count() > 0
var template = page.Template.Get();         // One → T (throws if not set; guard first)
page.Template.TryGet(out var t);            // safe variant
foreach (var child in node.Children.Get())  // Many → IEnumerable<T>

In queries prefer Include(...) over lazy .Get() to avoid N+1.

CRUD through NodeStore

var page = db.Create<IPage>();             // in-memory, NOT yet persisted
db.Insert(page);                            // or CreateAndInsert<IPage>(p => { ... })
db.Update(existing);                        // UpdateOrFail / UpdateIfExists / Upsert / ForceUpsert
db.Delete(node);                            // also Delete(Guid id) / Delete(IEnumerable<Guid>)
var p = db.Get<IPage>(id);                  // throws if missing
db.TryGet<IPage>(id, out var page2);        // safe
bool exists = db.Exists<IPage>(id);

Relations are set/cleared as their own operations (property-selector lambda):

db.SetRelation(page, p => p.Template, templateId);  // one-end: replaces
db.AddRelation(template, t => t.Pages, page);        // add to many-end
db.RemoveRelation(template, t => t.Pages, page);     // remove one link
db.ClearAndSetRelation(node, x => x.Many, newSet);   // replace whole set

Write methods accept flushToDisk (default false) and have …Async counterparts. Mutating a property on a loaded node still requires an Update call to persist.

Querying

db.Query<T>() returns IQueryOfNodes<T,T> — fluent and expression-based; nothing runs until a terminal call:

var results = db.Query<IPage>()
    .Where(p => p.MetaTitle != null)
    .Include(p => p.Template)            // eager-load; ThenInclude to go deeper
    .OrderBy(p => p.Name)
    .Page(0, 20)                          // pageIndex0based, pageSize (or Take/Skip)
    .Execute();                           // ResultSet<IPage>; .ToList()/.ToArray() also

var one  = db.Query<IPage>().Where(p => p.Meta.Address == slug).FirstOrDefault();
long n   = db.Query<IPage>().Count();

Also: WhereSearch/Search (BM25 + optional semantic/vector), WhereRelates/WhereNotRelates/WhereRelatesAny, WhereIn, WhereTypes, Facets(), Sum(...), SelectId(), a string-based Where("a => …") overload, and db.TryGetFromAddress<IPage>(address, out var page) for URL → node. Query context controls culture/visibility/revisions: db.Context.Culture("nb-NO").Hidden().Admin().Create(). Default queries exclude hidden nodes.

Transaction plugins (triggers)

Cross-cutting node behavior goes in a plugin. Derive NodeTransactionPlugin<T>, override OnBeforeNodeAction / lifecycle hooks, register with db.RegisterTransactionPlugin(...). Example: UrlAddressPlugin : NodeTransactionPlugin<IPage> generates a unique URL slug on upsert via transaction.UpdateAddress(...). Switch on the NodeOperation enum (Upsert, Insert*, Update*, ForceUpsert, Delete*).

Dynamic URL routing (Relatude.CMS.Engine.Web)

  • RelatudeDynamicRouting : DynamicRouteValueTransformer maps URL → {controller, action, id} via IRelatudeAddressResolver (→ RequestInfo), with per-request state in IRelatudeRequestContext.
  • IRelatudeUrlService.GetUrl(IPage) reverses it: stored Meta.Address, then a template-based action link, then /rnode/{id} fallback.
  • RelatudeUrlHelper.GetUniqueUrlSlug(name, db) generates collision-free slugs.

Notes

  • Create<T>() does not persist — follow with Insert/CreateAndInsert; call Update after mutating a loaded node.
  • Relation ends aren't Lists — use .Get() / .IsSet() / .TryGet().
  • Relation changes go through SetRelation/AddRelation/RemoveRelation, not property assignment.
  • This is a library with no entry point; engine start and schema live in the host app and the /relatude.db admin UI.
  • Relatude.DB source/API reference: https://github.com/Relatude/Relatude.DB
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Relatude.DB.CMS:

Package Downloads
Relatude.DB.Ecom

Relatude E-commerce backend API for Relatude.DB. Builds on the Relatude.CMS package.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.21 116 6/26/2026
1.0.20 120 6/26/2026