DataLinq
DataLinq is an immutable-first, source-generated ORM for .NET. It is designed around a narrow but useful idea: push model shape and metadata into generated code so runtime reads, relations, mutations, validation, and caching can be explicit and predictable.
It is a strong fit when you want:
- immutable models instead of ambient mutable state
- generated types instead of reflection-heavy runtime mapping
- aggressive row and relation caching
- a query surface that is grounded in tests instead of ORM wishful thinking
- schema validation and conservative diff tooling without committing to automatic migrations
Why It Exists
Most ORMs optimize for convenience first and clarity second. DataLinq makes a different trade.
It pushes more work to code generation and metadata so the runtime can stay simpler:
- reads return immutable instances
- relations are cache-aware and lazy
- writes go through explicit mutation objects and transactions
- supported LINQ is deliberately narrower than "all of LINQ", which keeps the behavior easier to reason about
That trade is especially useful in read-heavy applications where object identity, predictable behavior, and low repeated allocation matter.
The Mental Model
flowchart LR
Schema[("Database schema")] --> CLI["DataLinq CLI"]
CLI --> Models["Abstract source models"]
Models --> Generator["Source generator"]
Generator --> Generated["Immutable and mutable generated types"]
Generated --> Runtime["Runtime API"]
Runtime --> Cache["Provider-key row and relation cache"]
Runtime --> Provider["SQLite / MySQL / MariaDB provider"]
Provider --> Schema
The key is that generated code is not decoration. It is part of the runtime contract: generated metadata, generated factories, generated relation handles, and generated key accessors all help keep runtime behavior boring in the best possible way.
What It Is Not
DataLinq is not trying to be a universal EF replacement, a full migration engine, or an arbitrary LINQ translator. The public docs describe the supported boundaries because those boundaries matter.
Start Here
If you are new to DataLinq, this is the shortest sensible path:
What Makes It Different
- Immutable by default: query results are immutable model instances.
- Source-generated model surface: DataLinq generates the concrete immutable and mutable types from model metadata.
- Cache-aware runtime: repeated reads and relation traversal can reuse cached rows instead of rebuilding objects over and over.
- Explicit mutation workflow: updates happen through mutable wrappers and transactions rather than hidden dirty tracking.
- Conservative query support: the docs only promise the LINQ shapes that are actually covered by tests.
Small Example
using DataLinq;
using DataLinq.MySql;
using MyApp.Models;
var db = new MySqlDatabase<AppDb>(connectionString);
var activeUsers = db.Query().Users
.Where(x => x.IsActive)
.OrderBy(x => x.UserId)
.ToList();
var user = db.Query().Users.Single(x => x.UserId == userId);
var updatedUser = user.Mutate(x => x.DisplayName = "Updated").Save();
That is the core DataLinq shape:
- query through generated table properties
- mutate through a mutable wrapper
- save to get back a fresh immutable instance
Documentation
Once you are through the onboarding flow, the docs split into the main working areas:
- Intro
- Getting Started
- Usage
- Diagnostics and Metrics
- Platform Compatibility
- Changelog
- Roadmap
- Providers
- Internals
- Contributing
GitHub
If you are browsing from the repository, the README stays focused on the GitHub experience. This page is the website-first landing page.