Relatude.DB.CMS
1.0.20
See the version list below for details.
dotnet add package Relatude.DB.CMS --version 1.0.20
NuGet\Install-Package Relatude.DB.CMS -Version 1.0.20
<PackageReference Include="Relatude.DB.CMS" Version="1.0.20" />
<PackageVersion Include="Relatude.DB.CMS" Version="1.0.20" />
<PackageReference Include="Relatude.DB.CMS" />
paket add Relatude.DB.CMS --version 1.0.20
#r "nuget: Relatude.DB.CMS, 1.0.20"
#:package Relatude.DB.CMS@1.0.20
#addin nuget:?package=Relatude.DB.CMS&version=1.0.20
#tool nuget:?package=Relatude.DB.CMS&version=1.0.20
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 inRelatude.Ecom(relatude-ecomskill), 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 = ...)].
Navigating relations on a loaded node
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 : DynamicRouteValueTransformermaps URL →{controller, action, id}viaIRelatudeAddressResolver(→RequestInfo), with per-request state inIRelatudeRequestContext.IRelatudeUrlService.GetUrl(IPage)reverses it: storedMeta.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 withInsert/CreateAndInsert; callUpdateafter 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.dbadmin UI. - Relatude.DB source/API reference: https://github.com/Relatude/Relatude.DB
| Product | Versions 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. |
-
net8.0
- Microsoft.AspNetCore.Mvc.Core (>= 2.3.11)
- Relatude.DB.Server (>= 0.2.0.109-alpha)
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.