From fde72ca02b777ba0340d8c44d0d5cc20b77c3709 Mon Sep 17 00:00:00 2001 From: Svyatoslav Danyliv Date: Fri, 10 Apr 2026 13:56:17 +0300 Subject: [PATCH 1/8] Reorganize site with three-section layout: Documentation, Articles, API - Create landing page with hero section and three main navigation cards - Move guide content from articles/ to new documentation/ section with hub page - Repurpose articles/ for blog/news content (Release Notes) - Add Documentation to top-level navigation - Update docfx.json content globs for new structure - Add custom CSS for card layouts, navbar logo sizing, and hover effects - Add CLAUDE.md with project guidance Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 63 ++++++++++++++ source/articles/index.md | 19 +++++ source/articles/toc.yml | 16 ---- source/docfx.json | 3 +- source/{articles => documentation}/CLI.md | 0 source/{articles => documentation}/FAQ.md | 0 source/{articles => documentation}/T4.md | 0 .../{articles => documentation}/cli-help.txt | 0 .../general/Managing-data-connection.md | 0 .../general/Video.md | 0 .../general/databases.md | 0 .../general/interceptors.md | 0 .../general/metrics.md | 0 .../general/toc.yml | 0 .../get-started/asp-dotnet-core/index.md | 0 .../get-started/full-dotnet/existing-db.md | 0 .../full-dotnet/static/output-existing-db.png | Bin .../get-started/install/index.md | 0 .../get-started/toc.yml | 0 ...-linq2db-convert-custom-net-code-to-sql.md | 0 .../how-to/toc.yml | 0 ...g-mapvalue-attribute-to-control-mapping.md | 0 source/documentation/index.md | 69 ++++++++++++++++ source/{articles => documentation}/links.md | 0 .../project/Issue-reporting.md | 0 .../project/contrib.md | 0 .../project/toc.yml | 0 .../sql/Bulk-Copy.md | 0 source/{articles => documentation}/sql/CTE.md | 0 .../sql/Join-Operators.md | 0 .../sql/Query-Extensions.md | 0 .../Window-Functions-(Analytic-Functions).md | 0 .../sql/merge/Merge-API-Background.md | 0 .../sql/merge/Merge-API-Description.md | 0 .../sql/merge/Merge-API-Migration.md | 0 .../sql/merge/Merge-API.md | 0 .../sql/merge/toc.yml | 0 .../{articles => documentation}/sql/toc.yml | 0 source/documentation/toc.yml | 16 ++++ source/index.md | 78 +++++++++++++++++- source/templates/custom/public/main.css | 49 ++++++++++- source/toc.yml | 5 +- 42 files changed, 296 insertions(+), 22 deletions(-) create mode 100644 CLAUDE.md create mode 100644 source/articles/index.md rename source/{articles => documentation}/CLI.md (100%) rename source/{articles => documentation}/FAQ.md (100%) rename source/{articles => documentation}/T4.md (100%) rename source/{articles => documentation}/cli-help.txt (100%) rename source/{articles => documentation}/general/Managing-data-connection.md (100%) rename source/{articles => documentation}/general/Video.md (100%) rename source/{articles => documentation}/general/databases.md (100%) rename source/{articles => documentation}/general/interceptors.md (100%) rename source/{articles => documentation}/general/metrics.md (100%) rename source/{articles => documentation}/general/toc.yml (100%) rename source/{articles => documentation}/get-started/asp-dotnet-core/index.md (100%) rename source/{articles => documentation}/get-started/full-dotnet/existing-db.md (100%) rename source/{articles => documentation}/get-started/full-dotnet/static/output-existing-db.png (100%) rename source/{articles => documentation}/get-started/install/index.md (100%) rename source/{articles => documentation}/get-started/toc.yml (100%) rename source/{articles => documentation}/how-to/teach-linq2db-convert-custom-net-code-to-sql.md (100%) rename source/{articles => documentation}/how-to/toc.yml (100%) rename source/{articles => documentation}/how-to/using-mapvalue-attribute-to-control-mapping.md (100%) create mode 100644 source/documentation/index.md rename source/{articles => documentation}/links.md (100%) rename source/{articles => documentation}/project/Issue-reporting.md (100%) rename source/{articles => documentation}/project/contrib.md (100%) rename source/{articles => documentation}/project/toc.yml (100%) rename source/{articles => documentation}/sql/Bulk-Copy.md (100%) rename source/{articles => documentation}/sql/CTE.md (100%) rename source/{articles => documentation}/sql/Join-Operators.md (100%) rename source/{articles => documentation}/sql/Query-Extensions.md (100%) rename source/{articles => documentation}/sql/Window-Functions-(Analytic-Functions).md (100%) rename source/{articles => documentation}/sql/merge/Merge-API-Background.md (100%) rename source/{articles => documentation}/sql/merge/Merge-API-Description.md (100%) rename source/{articles => documentation}/sql/merge/Merge-API-Migration.md (100%) rename source/{articles => documentation}/sql/merge/Merge-API.md (100%) rename source/{articles => documentation}/sql/merge/toc.yml (100%) rename source/{articles => documentation}/sql/toc.yml (100%) create mode 100644 source/documentation/toc.yml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6647057 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,63 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Documentation site for all LinqToDB projects, hosted at [linq2db.github.io](https://linq2db.github.io). Built with a **custom DocFX binary** (`docfx/docfx`) — not the standard NuGet/dotnet tool version. The custom build adds `globalPrefix` support to handle namespace conflicts across EF Core versions (dotnet/docfx#8966). + +## Build Commands + +```bash +# Update submodules to latest release (required before first build) +./submodules.cmd # runs: git submodule update --remote --merge + +# Local build — generates static site in _site/ +./local.cmd # runs build.ps1 with deploy=$false + +# Manual build +powershell ./build.ps1 -deploy $false # build only +powershell ./build.ps1 -deploy $true # build + deploy (needs GITHUB_PAT env var) +``` + +Requires **.NET SDK 10.0** (see `global.json`). The build pre-compiles `LinqToDB.FSharp` as a DocFX workaround before running `./docfx/docfx source/docfx.json`. + +## Documentation Structure + +- **`source/articles/`** — Hand-written documentation (Markdown + `toc.yml` per section) + - `general/` — Core concepts (connections, interceptors, metrics, database support) + - `get-started/` — Installation and setup guides + - `sql/` — SQL features (bulk copy, CTEs, MERGE, joins, window functions) + - `how-to/` — Task-oriented guides + - `project/` — Contributing, issue reporting +- **`source/api/`** — Auto-generated API docs (output of DocFX metadata extraction, not hand-edited) +- **`source/templates/custom/`** — CSS/JS overrides for DocFX modern template +- **`_site/`** — Generated static HTML output (gitignored) + +## Key Configuration Files + +- **`source/docfx.json`** — Main DocFX config: 17 metadata sources (API namespaces) + build settings +- **`source/toc.yml`** — Top-level navigation structure +- **`source/filter.yml`** — API filtering rules +- **`source/index.md`** — Homepage (includes `../submodules/linq2db/readme.md`) + +## Submodules + +Source code for API documentation lives in `submodules/`: +- **`linq2db`** (release branch) — Main ORM library, F# extensions, tools, scaffold, remote packages +- **`LinqToDB.Identity`** (master) — ASP.NET Identity provider +- **`linq2db.EntityFrameworkCore`** — EF Core 3/8/9/10 integration +- **`IdentityServer4.LinqToDB`** — IdentityServer4 integration + +Run `./submodules.cmd` after cloning or when API docs need updating. + +## CI/CD (Azure Pipelines) + +- **`azure-pipelines.yml`** — Triggers on master push: builds and deploys to linq2db.github.io +- **`azure-pipelines.build.yml`** — Triggers on PRs: build-only validation + +Both use Windows 2022 VM with .NET SDK 10.x. + +## Editor Conventions + +Per `.editorconfig`: tab indentation (size 4), CRLF line endings, final newline required. diff --git a/source/articles/index.md b/source/articles/index.md new file mode 100644 index 0000000..5e8236f --- /dev/null +++ b/source/articles/index.md @@ -0,0 +1,19 @@ +--- +title: Articles +_disableAffix: true +_disableNextArticle: true +--- + +# Articles + +
+
+
+
+

Release Notes

+

Release history, changelogs, and project roadmap.

+ +
+
+
+
diff --git a/source/articles/toc.yml b/source/articles/toc.yml index 13dc032..b0c87c5 100644 --- a/source/articles/toc.yml +++ b/source/articles/toc.yml @@ -1,18 +1,2 @@ - name: Release Notes href: https://github.com/linq2db/linq2db/wiki/Releases-and-Roadmap -- name: Get Started - href: get-started/toc.yml -- name: General Topics - href: general/toc.yml -- name: SQL - href: sql/toc.yml -- name: How To - href: how-to/toc.yml -- name: CLI Scaffold Tool - href: CLI.md -- name: T4 Templates (Obsoleted) - href: T4.md -- name: FAQ - href: FAQ.md -- name: Project - href: project/toc.yml \ No newline at end of file diff --git a/source/docfx.json b/source/docfx.json index e95fb8f..53eef6b 100644 --- a/source/docfx.json +++ b/source/docfx.json @@ -220,6 +220,7 @@ "files": [ "api/**/*.{md,yml}", "articles/**/*.{md,yml}", + "documentation/**/*.{md,yml}", "toc.yml", "*.md" ] @@ -231,7 +232,7 @@ "images/**", "**.png", "**.jpg", - "articles/cli-help.txt", + "documentation/cli-help.txt", ".nojekyll" ] } diff --git a/source/articles/CLI.md b/source/documentation/CLI.md similarity index 100% rename from source/articles/CLI.md rename to source/documentation/CLI.md diff --git a/source/articles/FAQ.md b/source/documentation/FAQ.md similarity index 100% rename from source/articles/FAQ.md rename to source/documentation/FAQ.md diff --git a/source/articles/T4.md b/source/documentation/T4.md similarity index 100% rename from source/articles/T4.md rename to source/documentation/T4.md diff --git a/source/articles/cli-help.txt b/source/documentation/cli-help.txt similarity index 100% rename from source/articles/cli-help.txt rename to source/documentation/cli-help.txt diff --git a/source/articles/general/Managing-data-connection.md b/source/documentation/general/Managing-data-connection.md similarity index 100% rename from source/articles/general/Managing-data-connection.md rename to source/documentation/general/Managing-data-connection.md diff --git a/source/articles/general/Video.md b/source/documentation/general/Video.md similarity index 100% rename from source/articles/general/Video.md rename to source/documentation/general/Video.md diff --git a/source/articles/general/databases.md b/source/documentation/general/databases.md similarity index 100% rename from source/articles/general/databases.md rename to source/documentation/general/databases.md diff --git a/source/articles/general/interceptors.md b/source/documentation/general/interceptors.md similarity index 100% rename from source/articles/general/interceptors.md rename to source/documentation/general/interceptors.md diff --git a/source/articles/general/metrics.md b/source/documentation/general/metrics.md similarity index 100% rename from source/articles/general/metrics.md rename to source/documentation/general/metrics.md diff --git a/source/articles/general/toc.yml b/source/documentation/general/toc.yml similarity index 100% rename from source/articles/general/toc.yml rename to source/documentation/general/toc.yml diff --git a/source/articles/get-started/asp-dotnet-core/index.md b/source/documentation/get-started/asp-dotnet-core/index.md similarity index 100% rename from source/articles/get-started/asp-dotnet-core/index.md rename to source/documentation/get-started/asp-dotnet-core/index.md diff --git a/source/articles/get-started/full-dotnet/existing-db.md b/source/documentation/get-started/full-dotnet/existing-db.md similarity index 100% rename from source/articles/get-started/full-dotnet/existing-db.md rename to source/documentation/get-started/full-dotnet/existing-db.md diff --git a/source/articles/get-started/full-dotnet/static/output-existing-db.png b/source/documentation/get-started/full-dotnet/static/output-existing-db.png similarity index 100% rename from source/articles/get-started/full-dotnet/static/output-existing-db.png rename to source/documentation/get-started/full-dotnet/static/output-existing-db.png diff --git a/source/articles/get-started/install/index.md b/source/documentation/get-started/install/index.md similarity index 100% rename from source/articles/get-started/install/index.md rename to source/documentation/get-started/install/index.md diff --git a/source/articles/get-started/toc.yml b/source/documentation/get-started/toc.yml similarity index 100% rename from source/articles/get-started/toc.yml rename to source/documentation/get-started/toc.yml diff --git a/source/articles/how-to/teach-linq2db-convert-custom-net-code-to-sql.md b/source/documentation/how-to/teach-linq2db-convert-custom-net-code-to-sql.md similarity index 100% rename from source/articles/how-to/teach-linq2db-convert-custom-net-code-to-sql.md rename to source/documentation/how-to/teach-linq2db-convert-custom-net-code-to-sql.md diff --git a/source/articles/how-to/toc.yml b/source/documentation/how-to/toc.yml similarity index 100% rename from source/articles/how-to/toc.yml rename to source/documentation/how-to/toc.yml diff --git a/source/articles/how-to/using-mapvalue-attribute-to-control-mapping.md b/source/documentation/how-to/using-mapvalue-attribute-to-control-mapping.md similarity index 100% rename from source/articles/how-to/using-mapvalue-attribute-to-control-mapping.md rename to source/documentation/how-to/using-mapvalue-attribute-to-control-mapping.md diff --git a/source/documentation/index.md b/source/documentation/index.md new file mode 100644 index 0000000..d11e85a --- /dev/null +++ b/source/documentation/index.md @@ -0,0 +1,69 @@ +--- +title: Documentation +_disableAffix: true +_disableNextArticle: true +--- + +# Linq To DB Documentation + +
+
+
+
+

Get Started

+

Install Linq To DB and create your first project. Guides for ASP.NET Core and existing databases.

+ +
+
+
+ +
+
+
+

General Topics

+

Database support, managing connections, interceptors, metrics, and core concepts.

+ +
+
+
+ +
+
+
+

SQL

+

Join operators, CTEs, Bulk Copy, Window Functions, MERGE operations, and Query Extensions.

+ +
+
+
+ +
+
+
+

How-To Guides

+

Practical guides for custom SQL conversions, attribute mapping, and common tasks.

+ +
+
+
+ +
+
+
+

CLI Scaffold Tool

+

Generate data model from existing database using the command-line scaffold tool.

+ +
+
+
+ +
+
+
+

FAQ

+

Frequently asked questions and common issues.

+ +
+
+
+
diff --git a/source/articles/links.md b/source/documentation/links.md similarity index 100% rename from source/articles/links.md rename to source/documentation/links.md diff --git a/source/articles/project/Issue-reporting.md b/source/documentation/project/Issue-reporting.md similarity index 100% rename from source/articles/project/Issue-reporting.md rename to source/documentation/project/Issue-reporting.md diff --git a/source/articles/project/contrib.md b/source/documentation/project/contrib.md similarity index 100% rename from source/articles/project/contrib.md rename to source/documentation/project/contrib.md diff --git a/source/articles/project/toc.yml b/source/documentation/project/toc.yml similarity index 100% rename from source/articles/project/toc.yml rename to source/documentation/project/toc.yml diff --git a/source/articles/sql/Bulk-Copy.md b/source/documentation/sql/Bulk-Copy.md similarity index 100% rename from source/articles/sql/Bulk-Copy.md rename to source/documentation/sql/Bulk-Copy.md diff --git a/source/articles/sql/CTE.md b/source/documentation/sql/CTE.md similarity index 100% rename from source/articles/sql/CTE.md rename to source/documentation/sql/CTE.md diff --git a/source/articles/sql/Join-Operators.md b/source/documentation/sql/Join-Operators.md similarity index 100% rename from source/articles/sql/Join-Operators.md rename to source/documentation/sql/Join-Operators.md diff --git a/source/articles/sql/Query-Extensions.md b/source/documentation/sql/Query-Extensions.md similarity index 100% rename from source/articles/sql/Query-Extensions.md rename to source/documentation/sql/Query-Extensions.md diff --git a/source/articles/sql/Window-Functions-(Analytic-Functions).md b/source/documentation/sql/Window-Functions-(Analytic-Functions).md similarity index 100% rename from source/articles/sql/Window-Functions-(Analytic-Functions).md rename to source/documentation/sql/Window-Functions-(Analytic-Functions).md diff --git a/source/articles/sql/merge/Merge-API-Background.md b/source/documentation/sql/merge/Merge-API-Background.md similarity index 100% rename from source/articles/sql/merge/Merge-API-Background.md rename to source/documentation/sql/merge/Merge-API-Background.md diff --git a/source/articles/sql/merge/Merge-API-Description.md b/source/documentation/sql/merge/Merge-API-Description.md similarity index 100% rename from source/articles/sql/merge/Merge-API-Description.md rename to source/documentation/sql/merge/Merge-API-Description.md diff --git a/source/articles/sql/merge/Merge-API-Migration.md b/source/documentation/sql/merge/Merge-API-Migration.md similarity index 100% rename from source/articles/sql/merge/Merge-API-Migration.md rename to source/documentation/sql/merge/Merge-API-Migration.md diff --git a/source/articles/sql/merge/Merge-API.md b/source/documentation/sql/merge/Merge-API.md similarity index 100% rename from source/articles/sql/merge/Merge-API.md rename to source/documentation/sql/merge/Merge-API.md diff --git a/source/articles/sql/merge/toc.yml b/source/documentation/sql/merge/toc.yml similarity index 100% rename from source/articles/sql/merge/toc.yml rename to source/documentation/sql/merge/toc.yml diff --git a/source/articles/sql/toc.yml b/source/documentation/sql/toc.yml similarity index 100% rename from source/articles/sql/toc.yml rename to source/documentation/sql/toc.yml diff --git a/source/documentation/toc.yml b/source/documentation/toc.yml new file mode 100644 index 0000000..14e25c4 --- /dev/null +++ b/source/documentation/toc.yml @@ -0,0 +1,16 @@ +- name: Get Started + href: get-started/toc.yml +- name: General Topics + href: general/toc.yml +- name: SQL + href: sql/toc.yml +- name: How To + href: how-to/toc.yml +- name: CLI Scaffold Tool + href: CLI.md +- name: T4 Templates (Obsoleted) + href: T4.md +- name: FAQ + href: FAQ.md +- name: Project + href: project/toc.yml diff --git a/source/index.md b/source/index.md index b1d297d..e5f5ece 100644 --- a/source/index.md +++ b/source/index.md @@ -1 +1,77 @@ -[!include[intro](../submodules/linq2db/readme.md)] +--- +title: Linq To DB +_disableToc: true +_disableAffix: true +_disableBreadcrumb: true +_disableNextArticle: true +--- + +
+
+ +

Linq To DB

+

The fastest LINQ database access library offering a simple, light, fast, and type-safe layer between your POCO objects and your database.

+Get Started +View on GitHub +
+
+ +
+
+ +
+
+
+
+

Documentation

+

Comprehensive guides covering installation, core concepts, SQL features, and best practices.

+Browse docs +
+
+
+ +
+
+
+
+

Articles

+

Release notes, announcements, and news from the Linq To DB project.

+Read articles +
+
+
+ +
+
+
+
+

API Reference

+

Complete API documentation for all Linq To DB packages, extensions, and integrations.

+Explore API +
+
+
+ +
+
+ +
+
+ +
+

High Performance

+

Generates optimized SQL queries with minimal overhead. No reflection at runtime, no heavy object tracking.

+
+ +
+

Multi-Database

+

Supports SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird, ClickHouse, and many more databases.

+
+ +
+

Full LINQ Support

+

Write type-safe queries using standard LINQ syntax with advanced features like CTEs, Window Functions, and MERGE.

+
+ +
+
diff --git a/source/templates/custom/public/main.css b/source/templates/custom/public/main.css index 1b1f898..e064fd5 100644 --- a/source/templates/custom/public/main.css +++ b/source/templates/custom/public/main.css @@ -1 +1,48 @@ -.dropdown-menu { white-space:nowrap } \ No newline at end of file +.dropdown-menu { white-space:nowrap } + +/* Navbar logo */ +#logo { + height: 24px; + width: auto; + margin-right: 8px; +} + +/* Landing page hero */ +.landing-hero { + background: var(--bs-body-bg); + border-bottom: 1px solid var(--bs-border-color); +} + +.landing-logo { + width: 48px; + height: 48px; +} + +/* Landing page cards */ +.landing-card { + border: 1px solid var(--bs-border-color); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.landing-card:hover { + transform: translateY(-4px); + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1); +} + +/* Documentation hub cards */ +.doc-card { + border: 1px solid var(--bs-border-color); + transition: transform 0.2s ease, box-shadow 0.2s ease; + cursor: pointer; +} + +.doc-card:hover { + transform: translateY(-2px); + box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.08); + border-color: var(--bs-primary); +} + +.doc-card .card-title i { + margin-right: 0.5rem; + color: var(--bs-primary); +} diff --git a/source/toc.yml b/source/toc.yml index 70da596..e857380 100644 --- a/source/toc.yml +++ b/source/toc.yml @@ -1,3 +1,5 @@ +- name: Documentation + href: documentation/ - name: Articles href: articles/ - name: API Documentation @@ -33,8 +35,5 @@ href: api/linq2db.efcore.9/ - name: Linq To DB for Entity Framework Core 10 href: api/linq2db.efcore.10/ -# don't produce anything -# - name: Linq To DB Compat -# href: api/linq2db.compat/ - name: Linq To DB ASP.NET Identity Provider href: api/linq2db.identity/ From 7812f912a81320a2d9faa6153e546d2fc86fddb9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Danyliv Date: Fri, 10 Apr 2026 14:13:08 +0300 Subject: [PATCH 2/8] Modern landing page with hero, code preview, and gradient styling - Redesign hero section with dark gradient background and split layout - Add syntax-highlighted C# code sample with terminal-style chrome - Replace basic Bootstrap cards with custom styled navigation cards - Add four-column feature highlights with gradient icon badges - Full-width sections breaking out of DocFX container - Dark/light mode aware via CSS variables - Responsive layout with mobile-first breakpoints - Fix code block formatting (use entities to prevent paragraph wrapping) Co-Authored-By: Claude Opus 4.6 (1M context) --- source/index.md | 129 +++++++--- source/templates/custom/public/main.css | 320 +++++++++++++++++++++++- 2 files changed, 395 insertions(+), 54 deletions(-) diff --git a/source/index.md b/source/index.md index e5f5ece..2f5a17e 100644 --- a/source/index.md +++ b/source/index.md @@ -6,72 +6,119 @@ _disableBreadcrumb: true _disableNextArticle: true --- -
+
+
- -

Linq To DB

-

The fastest LINQ database access library offering a simple, light, fast, and type-safe layer between your POCO objects and your database.

-Get Started -View on GitHub +
+
+
Open Source ORM for .NET
+

Data access
made simple

+

The fastest LINQ database access library. A lightweight, type-safe layer between your POCO objects and your database with full SQL support.

+ +
+dotnet add package linq2db
+
+
+
+
+ + + +Program.cs +
+
using LinqToDB;
+using LinqToDB.Data;
+
using var db = new DataConnection(options);
+
var query =
+    from p in db.GetTable<Product>()
+    where p.Category == "Electronics"
+       && p.Price > 99.99m
+    orderby p.Name
+    select new { p.Name, p.Price };
+
await foreach (var item in query.AsAsyncEnumerable())
+    Console.WriteLine($"{item.Name}: {item.Price}");
+
+
+
+
+
+
-
+
+
-
-
-
-

Documentation

-

Comprehensive guides covering installation, core concepts, SQL features, and best practices.

-Browse docs -
-
+ +
+

Documentation

+

Installation, core concepts, SQL features, configuration guides, and best practices.

+Browse docs +
-
-
-
-

Articles

-

Release notes, announcements, and news from the Linq To DB project.

-Read articles -
-
+ +
+

Articles

+

Release notes, announcements, and news from the Linq To DB project.

+Read articles +
-
-
-
-

API Reference

-

Complete API documentation for all Linq To DB packages, extensions, and integrations.

-Explore API + +
+

API Reference

+

Complete API documentation for all packages, extensions, and integrations.

+Explore API +
+
+
+ +
+
+
+
+
+
+

High Performance

+

Generates optimized SQL with minimal overhead. No reflection at runtime, no heavy object tracking.

-
-
- -
-

High Performance

-

Generates optimized SQL queries with minimal overhead. No reflection at runtime, no heavy object tracking.

+
+
+
+

Multi-Database

+

SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird, ClickHouse, and many more.

+
-
-

Multi-Database

-

Supports SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird, ClickHouse, and many more databases.

+
+
+
+

Full LINQ

+

Type-safe queries with advanced SQL: CTEs, Window Functions, MERGE, Bulk Copy, and more.

+
-
-

Full LINQ Support

-

Write type-safe queries using standard LINQ syntax with advanced features like CTEs, Window Functions, and MERGE.

+
+
+
+

Extensible

+

EF Core integration, ASP.NET Identity, gRPC, SignalR, and HTTP remoting out of the box.

+
+
diff --git a/source/templates/custom/public/main.css b/source/templates/custom/public/main.css index e064fd5..69ead4b 100644 --- a/source/templates/custom/public/main.css +++ b/source/templates/custom/public/main.css @@ -7,29 +7,292 @@ margin-right: 8px; } -/* Landing page hero */ +/* ============================================================ + Landing page + ============================================================ */ + +/* Hero — full-width breakout */ .landing-hero { - background: var(--bs-body-bg); - border-bottom: 1px solid var(--bs-border-color); + margin: -1.5rem calc(-50vw + 50%) 0; + padding: 5rem 0 4rem; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 40%, #0f3460 100%); + color: #e8e8f0; + overflow: hidden; + position: relative; +} + +[data-bs-theme="light"] .landing-hero { + background: linear-gradient(135deg, #0f3460 0%, #16213e 40%, #1a1a2e 100%); +} + +.landing-hero-inner { + position: relative; + z-index: 1; +} + +.landing-badge { + display: inline-block; + padding: 0.35rem 1rem; + border-radius: 2rem; + font-size: 0.85rem; + font-weight: 500; + letter-spacing: 0.02em; + background: rgba(255,255,255,0.1); + border: 1px solid rgba(255,255,255,0.15); + color: #a8b4ff; +} + +.landing-title { + font-size: 3.5rem; + font-weight: 800; + line-height: 1.1; + letter-spacing: -0.02em; + margin-bottom: 1.25rem; + color: #fff; +} + +.landing-accent { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.landing-subtitle { + font-size: 1.15rem; + line-height: 1.7; + color: #b0b8d0; + max-width: 480px; + margin-bottom: 2rem; +} + +.landing-actions { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; +} + +.landing-btn-primary { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border: none; + font-weight: 600; + padding: 0.75rem 1.75rem; + border-radius: 0.5rem; + transition: transform 0.2s, box-shadow 0.2s; +} + +.landing-btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.landing-btn-secondary { + border-color: rgba(255,255,255,0.25); + color: #e0e0f0; + font-weight: 600; + padding: 0.75rem 1.75rem; + border-radius: 0.5rem; +} + +.landing-btn-secondary:hover { + background: rgba(255,255,255,0.1); + border-color: rgba(255,255,255,0.4); + color: #fff; +} + +/* NuGet install box */ +.landing-install { + display: inline-block; + background: rgba(0,0,0,0.3); + border: 1px solid rgba(255,255,255,0.1); + border-radius: 0.5rem; + padding: 0.5rem 1.25rem; + font-family: var(--bs-font-monospace); + font-size: 0.9rem; + color: #a8b4ff; + letter-spacing: 0.01em; +} + +.landing-install code { + color: inherit; + background: none; + padding: 0; +} + +/* Code block in hero */ +.landing-code { + background: rgba(0,0,0,0.35); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 0.75rem; + overflow: hidden; + backdrop-filter: blur(10px); + box-shadow: 0 20px 60px rgba(0,0,0,0.3); +} + +.landing-code-header { + display: flex; + align-items: center; + gap: 6px; + padding: 0.75rem 1rem; + background: rgba(255,255,255,0.05); + border-bottom: 1px solid rgba(255,255,255,0.06); +} + +.landing-code-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: rgba(255,255,255,0.15); +} + +.landing-code-dot:nth-child(1) { background: #ff5f57; } +.landing-code-dot:nth-child(2) { background: #ffbd2e; } +.landing-code-dot:nth-child(3) { background: #28ca41; } + +.landing-code-file { + margin-left: 0.5rem; + font-size: 0.8rem; + color: rgba(255,255,255,0.4); + font-family: var(--bs-font-monospace); } -.landing-logo { - width: 48px; - height: 48px; +.landing-code-body { + margin: 0; + padding: 1.25rem; + font-size: 0.82rem; + line-height: 1.65; + color: #c8cee0; + overflow-x: auto; + background: transparent !important; + border: none !important; +} + +/* Syntax colors */ +.code-keyword { color: #c792ea; } +.code-type { color: #82aaff; } +.code-string { color: #c3e88d; } +.code-number { color: #f78c6c; } + +/* ============================================================ + Landing cards section + ============================================================ */ +.landing-cards { + margin: 0 calc(-50vw + 50%); + padding: 4rem 0; + background: var(--bs-body-bg); } -/* Landing page cards */ .landing-card { + display: flex; + flex-direction: column; + height: 100%; + padding: 2rem; + border-radius: 1rem; + background: var(--bs-body-bg); border: 1px solid var(--bs-border-color); - transition: transform 0.2s ease, box-shadow 0.2s ease; + text-decoration: none; + color: var(--bs-body-color); + transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; } .landing-card:hover { - transform: translateY(-4px); - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1); + transform: translateY(-6px); + box-shadow: 0 12px 40px rgba(102, 126, 234, 0.12); + border-color: #667eea; + color: var(--bs-body-color); + text-decoration: none; +} + +.landing-card-icon-wrap { + width: 56px; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0.75rem; + background: linear-gradient(135deg, rgba(102,126,234,0.1) 0%, rgba(118,75,162,0.1) 100%); + margin-bottom: 1.25rem; + font-size: 1.5rem; + color: #667eea; +} + +.landing-card h3 { + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.landing-card p { + flex: 1; + color: var(--bs-secondary-color); + font-size: 0.95rem; + line-height: 1.6; + margin-bottom: 1rem; +} + +.landing-card-link { + font-weight: 600; + font-size: 0.9rem; + color: #667eea; +} + +.landing-card:hover .landing-card-link { + color: #764ba2; +} + +.landing-card-link .bi { + transition: transform 0.2s; +} + +.landing-card:hover .landing-card-link .bi { + transform: translateX(4px); +} + +/* ============================================================ + Features section + ============================================================ */ +.landing-features { + margin: 0 calc(-50vw + 50%); + padding: 4rem 0 5rem; + background: var(--bs-tertiary-bg); + border-top: 1px solid var(--bs-border-color); +} + +.landing-feature { + text-align: center; + padding: 1rem; } -/* Documentation hub cards */ +.landing-feature-icon { + width: 52px; + height: 52px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 0.75rem; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: #fff; + font-size: 1.3rem; + margin-bottom: 1rem; +} + +.landing-feature h4 { + font-size: 1.1rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.landing-feature p { + font-size: 0.9rem; + color: var(--bs-secondary-color); + line-height: 1.6; + margin: 0; +} + +/* ============================================================ + Documentation hub cards (documentation/index.md) + ============================================================ */ .doc-card { border: 1px solid var(--bs-border-color); transition: transform 0.2s ease, box-shadow 0.2s ease; @@ -39,10 +302,41 @@ .doc-card:hover { transform: translateY(-2px); box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.08); - border-color: var(--bs-primary); + border-color: #667eea; } .doc-card .card-title i { margin-right: 0.5rem; - color: var(--bs-primary); + color: #667eea; +} + +/* ============================================================ + Responsive + ============================================================ */ +@media (max-width: 991.98px) { + .landing-title { + font-size: 2.5rem; + } + + .landing-hero { + padding: 3rem 0; + } + + .landing-code { + margin-top: 2rem; + } +} + +@media (max-width: 575.98px) { + .landing-title { + font-size: 2rem; + } + + .landing-actions { + flex-direction: column; + } + + .landing-actions .btn { + width: 100%; + } } From 42eda4bdac5f46a93234b540ddf27c92727bbfb2 Mon Sep 17 00:00:00 2001 From: Svyatoslav Danyliv Date: Fri, 10 Apr 2026 14:25:49 +0300 Subject: [PATCH 3/8] Enable edit links for documentation pages - Remove global _disableContribution flag - Add _gitContribute pointing to linq2db/docs repo on master branch - Disable edit links on API pages only (auto-generated, not hand-edited) - Documentation pages now show "Edit this page" linking to GitHub source Co-Authored-By: Claude Opus 4.6 (1M context) --- source/docfx.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/docfx.json b/source/docfx.json index 53eef6b..cd5446e 100644 --- a/source/docfx.json +++ b/source/docfx.json @@ -248,7 +248,15 @@ "_appFaviconPath": "images/icon.ico", "_enableSearch": true, "_disableNewTab": false, - "_disableContribution": true + "_gitContribute": { + "repo": "https://github.com/linq2db/docs", + "branch": "master" + } + }, + "fileMetadata": { + "_disableContribution": { + "api/**/**.yml": true + } }, "fileMetadataFiles": [], "template": [ From d553bd510e35ee3c12474ea1b32fc5e15f3e7cbd Mon Sep 17 00:00:00 2001 From: Svyatoslav Danyliv Date: Fri, 10 Apr 2026 15:41:40 +0300 Subject: [PATCH 4/8] Add interactive SQL demo with 7 tabbed examples on landing page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Query: basic WHERE + ORDER BY + SELECT - Associations: navigation properties → LEFT JOIN + subquery COUNT - ExpressionMethod: reusable [ExpressionMethod] with let keyword - CTE: named CTE reused in both join and subquery - Window Functions: ROW_NUMBER, RANK, SUM OVER with PARTITION BY - Merge: MERGE INTO with WHEN MATCHED/NOT MATCHED (SQL Server) - Temp Table: CreateTempTable with Enumerable.Range + join All SQL is real output from linq2db's ToSqlQuery() API. Tabs use pure CSS (radio inputs + :checked selectors, no JavaScript). Add tools/sql-demo/ with SqlDemoGenerator.cs test file and README.md maintenance guide for extending/rebuilding demo tabs. Update CLAUDE.md with new site structure and demo docs reference. Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 18 +- source/index.md | 341 ++++++++++++++++++++++-- source/templates/custom/public/main.css | 132 +++++++-- tools/sql-demo/README.md | 104 ++++++++ tools/sql-demo/SqlDemoGenerator.cs | 256 ++++++++++++++++++ 5 files changed, 802 insertions(+), 49 deletions(-) create mode 100644 tools/sql-demo/README.md create mode 100644 tools/sql-demo/SqlDemoGenerator.cs diff --git a/CLAUDE.md b/CLAUDE.md index 6647057..524c6b4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,22 +24,32 @@ Requires **.NET SDK 10.0** (see `global.json`). The build pre-compiles `LinqToDB ## Documentation Structure -- **`source/articles/`** — Hand-written documentation (Markdown + `toc.yml` per section) - - `general/` — Core concepts (connections, interceptors, metrics, database support) +- **`source/index.md`** — Landing page with hero, interactive SQL demo, navigation cards +- **`source/documentation/`** — Structured guides (Markdown + `toc.yml` per section) - `get-started/` — Installation and setup guides + - `general/` — Core concepts (connections, interceptors, metrics, database support) - `sql/` — SQL features (bulk copy, CTEs, MERGE, joins, window functions) - `how-to/` — Task-oriented guides - `project/` — Contributing, issue reporting +- **`source/articles/`** — Blog/news content (release notes) - **`source/api/`** — Auto-generated API docs (output of DocFX metadata extraction, not hand-edited) - **`source/templates/custom/`** — CSS/JS overrides for DocFX modern template - **`_site/`** — Generated static HTML output (gitignored) +## Landing Page SQL Demo + +The landing page has a tabbed demo showing C# LINQ → generated SQL. See **`tools/sql-demo/README.md`** for full maintenance guide. Key points: + +- SQL is pre-generated using `ToSqlQuery()` from the linq2db library +- Test file: `tools/sql-demo/SqlDemoGenerator.cs` — copy to `linq2db/Tests/Tests.Playground/` to run +- Tabs are pure CSS (radio inputs + `:checked` selectors), no JavaScript +- Use ` ` instead of blank lines inside `
` blocks (DocFX wraps blank lines in `

` tags) + ## Key Configuration Files - **`source/docfx.json`** — Main DocFX config: 17 metadata sources (API namespaces) + build settings -- **`source/toc.yml`** — Top-level navigation structure +- **`source/toc.yml`** — Top-level navigation (Documentation, Articles, API) - **`source/filter.yml`** — API filtering rules -- **`source/index.md`** — Homepage (includes `../submodules/linq2db/readme.md`) ## Submodules diff --git a/source/index.md b/source/index.md index 2f5a17e..defd9d7 100644 --- a/source/index.md +++ b/source/index.md @@ -23,24 +23,333 @@ _disableNextArticle: true

-
-
- - - -Program.cs -
-
using LinqToDB;
-using LinqToDB.Data;
-
using var db = new DataConnection(options);
-
var query =
+
+ + + + + + + +
+ + + + + + + +
+
+ +
+
+
+
C# LINQ
+
var query =
     from p in db.GetTable<Product>()
-    where p.Category == "Electronics"
-       && p.Price > 99.99m
+    where p.Price > 100
+       && p.CategoryId == 1
     orderby p.Name
-    select new { p.Name, p.Price };
-
await foreach (var item in query.AsAsyncEnumerable())
-    Console.WriteLine($"{item.Name}: {item.Price}");
+ select new + { + p.Name, + p.Price + };
+
+
+
Generated SQL
+
SELECT
+    [p].[Name],
+    [p].[Price]
+FROM
+    [Products] [p]
+WHERE
+    [p].[Price] > 100
+    AND [p].[CategoryId] = 1
+ORDER BY
+    [p].[Name]
+
+
+ + +
+
+
+
C# LINQ
+
var query =
+    from p in db.GetTable<Product>()
+    select new
+    {
+        Product  = p.Name,
+        Category = p.Category.Name,
+        Orders   = p.OrderItems.Count()
+    };
+
+
+
Generated SQL
+
SELECT
+    [p].[Name],
+    [a_Category].[Name],
+    (
+        SELECT COUNT(*)
+        FROM [OrderItems] [a_OrderItems]
+        WHERE
+            [p].[Id] = [a_OrderItems].[ProductId]
+    )
+FROM
+    [Products] [p]
+    LEFT JOIN [Categories] [a_Category]
+        ON [p].[CategoryId] = [a_Category].[Id]
+
+
+
+ +
+
+
+
C# LINQ
+
// Reusable SQL expression
+[ExpressionMethod(nameof(Impl))]
+static decimal TotalRevenue(Product p)
+    => throw new InvalidOperationException();
+
static Expression<Func<Product, decimal>>
+    Impl() => p => p.OrderItems
+        .Sum(oi => oi.Quantity * oi.UnitPrice);
+
// let avoids duplicate subqueries
+var query =
+    from p in db.GetTable<Product>()
+    let total = TotalRevenue(p)
+    where total > 1000
+    select new
+    {
+        p.Name,
+        Revenue = total
+    };
+
+
+
Generated SQL
+
SELECT
+    [t1].[Name],
+    [t1].[total]
+FROM
+    (
+        SELECT
+            (
+                SELECT SUM(
+                    CAST([oi].[Quantity]
+                      AS Decimal)
+                    * [oi].[UnitPrice])
+                FROM [OrderItems] [oi]
+                WHERE
+                    [p].[Id] = [oi].[ProductId]
+            ) as [total],
+            [p].[Name]
+        FROM
+            [Products] [p]
+    ) [t1]
+WHERE
+    [t1].[total] > 1000
+
+
+
+ +
+
+
+
C# LINQ
+
var topProducts =
+    db.GetTable<Product>()
+    .Where(p => p.Price > 50)
+    .Select(p => new
+    {
+        p.Id, p.Name,
+        p.Price, p.CategoryId
+    })
+    .AsCte("TopProducts");
+
// CTE reused in join + subquery
+var query =
+    from tp in topProducts
+    join c in db.GetTable<Category>()
+        on tp.CategoryId equals c.Id
+    select new
+    {
+        tp.Name, tp.Price,
+        Category = c.Name,
+        SameCategory = topProducts
+            .Count(x => x.CategoryId
+                == tp.CategoryId)
+    };
+
+
+
Generated SQL
+
WITH [TopProducts]
+    ([CategoryId], [Name], [Price])
+AS
+(
+    SELECT
+        [p].[CategoryId],
+        [p].[Name],
+        [p].[Price]
+    FROM [Products] [p]
+    WHERE [p].[Price] > 50
+)
+SELECT
+    [tp].[Name],
+    [tp].[Price],
+    [c].[Name],
+    (
+        SELECT COUNT(*)
+        FROM [TopProducts] [t1]
+        WHERE [t1].[CategoryId]
+            = [tp].[CategoryId]
+    )
+FROM
+    [TopProducts] [tp]
+    INNER JOIN [Categories] [c]
+        ON [tp].[CategoryId] = [c].[Id]
+
+
+
+ +
+
+
+
C# LINQ
+
var query =
+    from p in db.GetTable<Product>()
+    select new
+    {
+        p.Name,
+        p.Price,
+        Category = p.Category.Name,
+        RowNum = Sql.Ext.RowNumber()
+            .Over()
+            .PartitionBy(p.CategoryId)
+            .OrderByDesc(p.Price)
+            .ToValue(),
+        Total  = Sql.Ext.Sum(p.Price)
+            .Over()
+            .PartitionBy(p.CategoryId)
+            .ToValue()
+    };
+
+
+
Generated SQL
+
SELECT
+    [p].[Name],
+    [p].[Price],
+    [a_Category].[Name],
+    ROW_NUMBER() OVER(
+        PARTITION BY [p].[CategoryId]
+        ORDER BY [p].[Price] DESC),
+    SUM([p].[Price]) OVER(
+        PARTITION BY [p].[CategoryId])
+FROM
+    [Products] [p]
+    LEFT JOIN [Categories] [a_Category]
+        ON [p].[CategoryId]
+            = [a_Category].[Id]
+
+
+
+ +
+
+
+
C# LINQ
+
db.GetTable<Product>()
+    .Merge()
+    .Using(source)
+    .OnTargetKey()
+    .UpdateWhenMatched(
+        (target, src) => new Product
+        {
+            Name  = src.Name,
+            Price = src.Price,
+        })
+    .InsertWhenNotMatched(
+        src => new Product
+        {
+            Id         = src.Id,
+            Name       = src.Name,
+            Price      = src.Price,
+            CategoryId = src.CategoryId,
+        })
+    .Merge();
+
+
+
Generated SQL
+
MERGE INTO [Products] [Target]
+USING (VALUES
+    (1,N'Laptop',999,1),
+    (2,N'Tablet',499,1)
+) [Source]
+([Id],[Name],[Price],[CategoryId])
+ON ([Target].[Id] = [Source].[Id])
+
WHEN MATCHED THEN
+UPDATE SET
+    [Name]  = [Source].[Name],
+    [Price] = [Source].[Price]
+
WHEN NOT MATCHED THEN
+INSERT
+    ([Id],[Name],[Price],[CategoryId])
+VALUES
+    ([Source].[Id],
+     [Source].[Name],
+     [Source].[Price],
+     [Source].[CategoryId])
+;
+
+
+
+ +
+
+
+
C# Code
+
// Populate temp table from data
+var ids = Enumerable
+    .Range(1, 500)
+    .Select(i => new { Id = i });
+
using var tmp =
+    db.CreateTempTable(
+        "#FilterIds", ids);
+
// Join temp table in LINQ query
+var query =
+    from p in db.GetTable<Product>()
+    join t in tmp
+        on p.Id equals t.Id
+    orderby p.Name
+    select new
+    {
+        p.Name,
+        p.Price
+    };
+
+
+
Generated SQL
+
-- 1. CREATE TABLE
+CREATE TABLE [#FilterIds]
+(
+    [Id] int NOT NULL
+)
+
-- 2. BulkCopy 500 rows
+
-- 3. Query joins temp table
+SELECT
+    [p].[Name],
+    [p].[Price]
+FROM
+    [Products] [p]
+    INNER JOIN [#FilterIds] [t]
+        ON [p].[Id] = [t].[Id]
+ORDER BY
+    [p].[Name]
+
-- 4. Dispose drops table
+
+
+
+ diff --git a/source/templates/custom/public/main.css b/source/templates/custom/public/main.css index 69ead4b..e134225 100644 --- a/source/templates/custom/public/main.css +++ b/source/templates/custom/public/main.css @@ -120,8 +120,16 @@ padding: 0; } -/* Code block in hero */ -.landing-code { +/* Syntax colors */ +.code-keyword { color: #c792ea; } +.code-type { color: #82aaff; } +.code-string { color: #c3e88d; } +.code-number { color: #f78c6c; } + +/* ============================================================ + Interactive demo (pure CSS tabs) + ============================================================ */ +.demo-container { background: rgba(0,0,0,0.35); border: 1px solid rgba(255,255,255,0.08); border-radius: 0.75rem; @@ -130,49 +138,115 @@ box-shadow: 0 20px 60px rgba(0,0,0,0.3); } -.landing-code-header { +.demo-radio { display: none; } + +.demo-tabs { display: flex; - align-items: center; - gap: 6px; - padding: 0.75rem 1rem; + gap: 0; background: rgba(255,255,255,0.05); border-bottom: 1px solid rgba(255,255,255,0.06); + overflow-x: auto; + scrollbar-width: none; } -.landing-code-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: rgba(255,255,255,0.15); +.demo-tabs::-webkit-scrollbar { display: none; } + +.demo-tab { + padding: 0.6rem 1rem; + font-size: 0.78rem; + font-weight: 500; + color: rgba(255,255,255,0.4); + cursor: pointer; + white-space: nowrap; + border-bottom: 2px solid transparent; + transition: color 0.2s, border-color 0.2s; + user-select: none; } -.landing-code-dot:nth-child(1) { background: #ff5f57; } -.landing-code-dot:nth-child(2) { background: #ffbd2e; } -.landing-code-dot:nth-child(3) { background: #28ca41; } +.demo-tab:hover { + color: rgba(255,255,255,0.7); +} -.landing-code-file { - margin-left: 0.5rem; - font-size: 0.8rem; - color: rgba(255,255,255,0.4); - font-family: var(--bs-font-monospace); +/* Active tab highlighting via :checked */ +#demo-tab-1:checked ~ .demo-tabs label[for="demo-tab-1"], +#demo-tab-2:checked ~ .demo-tabs label[for="demo-tab-2"], +#demo-tab-3:checked ~ .demo-tabs label[for="demo-tab-3"], +#demo-tab-4:checked ~ .demo-tabs label[for="demo-tab-4"], +#demo-tab-5:checked ~ .demo-tabs label[for="demo-tab-5"], +#demo-tab-6:checked ~ .demo-tabs label[for="demo-tab-6"], +#demo-tab-7:checked ~ .demo-tabs label[for="demo-tab-7"] { + color: #a8b4ff; + border-bottom-color: #667eea; +} + +/* Panel visibility via :checked */ +.demo-panel { display: none; } + +#demo-tab-1:checked ~ .demo-panels .demo-panel-1, +#demo-tab-2:checked ~ .demo-panels .demo-panel-2, +#demo-tab-3:checked ~ .demo-panels .demo-panel-3, +#demo-tab-4:checked ~ .demo-panels .demo-panel-4, +#demo-tab-5:checked ~ .demo-panels .demo-panel-5, +#demo-tab-6:checked ~ .demo-panels .demo-panel-6, +#demo-tab-7:checked ~ .demo-panels .demo-panel-7 { + display: block; +} + +.demo-split { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 280px; +} + +@media (max-width: 767.98px) { + .demo-split { + grid-template-columns: 1fr; + } +} + +.demo-pane { + padding: 0; + position: relative; + overflow: hidden; } -.landing-code-body { +.demo-pane + .demo-pane { + border-left: 1px solid rgba(255,255,255,0.06); +} + +@media (max-width: 767.98px) { + .demo-pane + .demo-pane { + border-left: none; + border-top: 1px solid rgba(255,255,255,0.06); + } +} + +.demo-pane-label { + font-size: 0.65rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: rgba(255,255,255,0.3); + padding: 0.5rem 1rem 0; +} + +.demo-code { margin: 0; - padding: 1.25rem; - font-size: 0.82rem; - line-height: 1.65; + padding: 0.5rem 1rem 1rem; + font-size: 0.75rem; + line-height: 1.6; color: #c8cee0; - overflow-x: auto; background: transparent !important; border: none !important; + overflow-x: auto; + white-space: pre; } -/* Syntax colors */ -.code-keyword { color: #c792ea; } -.code-type { color: #82aaff; } -.code-string { color: #c3e88d; } -.code-number { color: #f78c6c; } +.demo-sql { + color: #e0dcc8; +} + +.code-comment { color: #676e95; font-style: italic; } /* ============================================================ Landing cards section diff --git a/tools/sql-demo/README.md b/tools/sql-demo/README.md new file mode 100644 index 0000000..d6d361b --- /dev/null +++ b/tools/sql-demo/README.md @@ -0,0 +1,104 @@ +# Landing Page SQL Demo — Maintenance Guide + +The landing page at `source/index.md` features an interactive tabbed demo showing C# LINQ code alongside the real SQL generated by linq2db. This document explains how to maintain and extend it. + +## Architecture + +- **C# side**: Hand-written syntax-highlighted HTML using `` classes (`code-keyword`, `code-type`, `code-string`, `code-number`, `code-comment`) +- **SQL side**: Real SQL output generated by linq2db's `ToSqlQuery()` API, then hand-formatted with the same span classes +- **Tabs**: Pure CSS — hidden `` elements + `:checked` sibling selectors in `main.css` (no JavaScript) + +## How to generate SQL for a new or updated tab + +### 1. Copy `SqlDemoGenerator.cs` to linq2db Tests.Playground + +```bash +cp tools/sql-demo/SqlDemoGenerator.cs ../linq2db/Tests/Tests.Playground/SqlDemoGenerator.cs +``` + +### 2. Edit the test to add/modify queries + +The file defines entity models (`Product`, `Category`, `Order`, `OrderItem`) and test methods that generate SQL using: + +```csharp +var sql = query.ToSqlQuery(new SqlGenerationOptions { InlineParameters = true }).Sql; +Console.WriteLine(sql); +``` + +- SQLite connection (`CreateSQLiteConnection()`) works for most queries +- Merge requires SQL Server — use `[IncludeDataSources(TestProvName.AllSqlServer)]` attribute +- TempTable creates DDL (side effects), so its SQL is documented manually on the landing page + +### 3. Run the tests + +```bash +cd ../linq2db +dotnet test Tests/Tests.Playground/Tests.Playground.csproj \ + -c Debug -f net10.0 \ + --filter "FullyQualifiedName~SqlDemoGenerator" \ + -v n --nologo 2>&1 | grep -A 50 "=== " +``` + +This prints the SQL for each test. Copy the output. + +### 4. Copy the test file back to docs for reference + +```bash +cp ../linq2db/Tests/Tests.Playground/SqlDemoGenerator.cs tools/sql-demo/SqlDemoGenerator.cs +``` + +### 5. Update the landing page + +Edit `source/index.md`. Each tab consists of: + +```html + +
+
+
+
C# LINQ
+
...syntax-highlighted C# here...
+
+
+
Generated SQL
+
...syntax-highlighted SQL here...
+
+
+
+``` + +### Adding a new tab + +1. Add a new `` with id `demo-tab-N` +2. Add a `