.NET 8 Web API + Blazor Server application with SQL DDL-driven data models and a DDL → YAML → C# pipeline.
Primary Goal: Use SQL DDL as the source of truth and generate
app.yaml+ C# models for dynamic customization.
./setup.shChoose Docker or native Linux installation.
dotnet tool install --global dotnet-ef --version 8.*make check # Lint scripts/Makefile, restore packages, build
make db-start # Start SQL Server (Docker only)
make run-ddl-pipeline # Generate app.yaml, models, and migration from SQL DDL
make migrate # Apply generated migration
make dev # Start dev server (https://localhost:7012 or http://localhost:5210)That's it! Navigate to https://localhost:7012 (or http://localhost:5210) to see the app.
The DdlParser converts your SQL Server DDL files into app.yaml format, which then generates C# entity models automatically.
your-schema.sql → DdlParser → app.yaml → ModelGenerator → DotNetWebApp.Models/Generated/*.cs → Migration → Build & Run
Create or replace schema.sql:
CREATE TABLE Companies (
Id INT PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(100) NOT NULL,
RegistrationNumber NVARCHAR(50) NOT NULL,
FoundedYear INT NULL
);
CREATE TABLE Employees (
Id INT PRIMARY KEY IDENTITY(1,1),
FirstName NVARCHAR(50) NOT NULL,
LastName NVARCHAR(50) NOT NULL,
Email NVARCHAR(100) NOT NULL,
Salary DECIMAL(18,2) NULL,
HireDate DATETIME2 NULL DEFAULT GETDATE(),
CompanyId INT NOT NULL,
FOREIGN KEY (CompanyId) REFERENCES Companies(Id)
);Then run:
make run-ddl-pipeline
make migrate
make devThe app now has Companies and Employees entities with:
- ✅ Auto-generated
DotNetWebApp.Models/Generated/Company.csandDotNetWebApp.Models/Generated/Employee.cs - ✅ Database tables with correct types, constraints, and relationships
- ✅ Navigation UI automatically includes Company and Employee links
- ✅ Generic REST API endpoints (
/api/companies,/api/employees) - ✅ Dynamic CRUD UI pages with data grids
Visit https://localhost:7012 (or http://localhost:5210) → click "Data" in sidebar → select Company or Employee
DotNetWebApp/
├── Components/
│ ├── Pages/ # Blazor routable pages (Home.razor, SpaApp.razor)
│ ├── Sections/ # SPA components (Dashboard, Settings, Entity, etc.)
│ └── Shared/ # Shared UI components
├── Controllers/ # API endpoints (EntitiesController, etc.)
├── Data/ # EF Core DbContext
├── DdlParser/ # 🆕 SQL DDL → YAML converter
│ ├── Program.cs
│ ├── CreateTableVisitor.cs
│ └── TypeMapper.cs
├── DotNetWebApp.Models/ # 🔄 Separate models assembly
│ ├── Generated/ # 🔄 Auto-generated entities from app.yaml
│ ├── AppDictionary/ # YAML model classes
│ └── *.cs # Options classes (AppCustomizationOptions, DataSeederOptions, etc.)
├── ModelGenerator/ # YAML → C# entity generator
├── Migrations/ # Generated EF Core migrations (current baseline checked in; pipeline regenerates)
├── Pages/ # Host and layout pages
├── Services/ # Business logic and DI services
├── Shared/ # Layout and shared UI
├── tests/ # Test projects
│ ├── DotNetWebApp.Tests/
│ └── ModelGenerator.Tests/
├── wwwroot/ # Static files (CSS, JS, images)
├── app.yaml # 📋 Generated data model definition (from SQL DDL)
├── schema.sql # Source SQL DDL
├── seed.sql # Seed data
├── Makefile # Build automation
└── dotnet-build.sh # SDK version wrapper script
- ✅
app.yamlis generated from SQL DDL and drives app metadata, theme, and data model shape - ✅
ModelGeneratorproduces entities inDotNetWebApp.Models/Generatedwith proper nullable types - ✅ Models extracted to separate
DotNetWebApp.Modelsassembly for better separation of concerns - ✅
AppDbContextauto-discovers entities via reflection - ✅ Phase 1 Complete (2026-01-27):
IEntityOperationServicewith compiled delegates (250x perf improvement)- Centralizes all CRUD operations for 200+ entities
EntitiesControllerreduced from 369 to 236 lines (36% reduction)- All reflection logic moved to service layer
- Comprehensive test suite added
- ✅
EntitiesControllerprovides dynamic REST endpoints - ✅
GenericEntityPage.razor+DynamicDataGrid.razorprovide dynamic CRUD UI - ✅ DdlParser converts SQL DDL files to
app.yamlformat - ✅ Migrations generated from SQL DDL pipeline (current baseline checked in; pipeline regenerates)
⚠️ Branding currently fromappsettings.json(can be moved to YAML)- ✅ Tenant schema switching via
X-Customer-Schemaheader (defaults todbo) - ✅ Dynamic API routes:
/api/entities/{entityName}and/api/entities/{entityName}/count - ✅ SPA example routes are optional via
AppCustomization:EnableSpaExample(default true)
| Command | Purpose |
|---|---|
make check |
Lint scripts/Makefile, restore, build |
make restore |
Restore app, generator, parser, and test projects |
make build |
Build main projects (Debug by default; set BUILD_CONFIGURATION) |
make build-all |
Build full solution including tests |
make build-release |
Release build for main projects |
make clean |
Clean build outputs and binlog |
make run-ddl-pipeline |
Parse schema.sql → app.yaml → models → migration → build |
make migrate |
Apply generated migration |
make seed |
Apply migration and seed data |
make dev |
Start dev server with hot reload (https://localhost:7012 / http://localhost:5210) |
make run |
Start server without hot reload |
make test |
Run DotNetWebApp.Tests and ModelGenerator.Tests |
make db-start |
Start SQL Server container (Docker) |
make db-stop |
Stop SQL Server container (Docker) |
make db-logs |
Tail SQL Server container logs |
make db-drop |
Drop local dev database in Docker |
make ms-status |
Check native SQL Server status |
make ms-start |
Start native SQL Server |
make ms-logs |
Tail native SQL Server logs |
make ms-drop |
Drop local dev database in native SQL Server |
make docker-build |
Build Docker image |
After modifying schema.sql or running the DDL parser:
# Start SQL Server
make db-start
# Generate migration from DDL, then apply it
make run-ddl-pipeline
make migrateseed.sql contains INSERT statements wrapped in IF NOT EXISTS guards so the script can safely run multiple times without duplicating rows. After running make run-ddl-pipeline + make migrate, populate the demo catalog data with:
make seedThen verify the data landed via the container's sqlcmd (see the Docker section for setup and example queries).
The new make seed target executes dotnet run --project DotNetWebApp.csproj -- --seed. That mode of the application applies the generated migration (Database.MigrateAsync()) and then runs seed.sql via the DataSeeder service, which uses ExecuteSqlRawAsync under the current connection string. Ensure the migration has been generated from the DDL pipeline before seeding. You can still run seed.sql manually (e.g., sqlcmd, SSMS) if you need fine-grained control.
make docker-builddocker run -d \
-p 8080:80 \
--name dotnetwebapp \
dotnetwebapp:latestRun the following commands from your host (the first must be executed as root inside the container) to install the SQL Server CLI tooling (sqlcmd) and verify the DotNetWebAppDb demo data:
docker exec -it --user root sqlserver-dev bash -lc "ACCEPT_EULA=Y apt-get update && \
ACCEPT_EULA=Y apt-get install -y mssql-tools unixodbc-dev"
docker exec -it sqlserver-dev \
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" \
-d DotNetWebAppDb -Q "SELECT Id, Name FROM dbo.Categories;"
docker exec -it sqlserver-dev \
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" \
-d DotNetWebAppDb -Q "SELECT Name, Price, CategoryId FROM dbo.Products;"These commands let you run seed.sql manually or troubleshoot seed data without installing SQL tooling on the host.
./setup.sh
# Choose "1" for Docker or "2" for native Linuxdotnet tool install --global dotnet-ef --version 8.*make checkmake db-start # Only needed for Docker
make run-ddl-pipeline
make migratemake devVisit https://localhost:7012 (or http://localhost:5210) in your browser.
File: schema.sql
CREATE TABLE Authors (
Id INT PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(100) NOT NULL,
Email NVARCHAR(100) NULL
);
CREATE TABLE Books (
Id INT PRIMARY KEY IDENTITY(1,1),
Title NVARCHAR(200) NOT NULL,
ISBN NVARCHAR(13) NOT NULL,
PublishedYear INT NULL,
AuthorId INT NOT NULL,
FOREIGN KEY (AuthorId) REFERENCES Authors(Id)
);make run-ddl-pipelineOutput: app.yaml now contains Author and Book entities.
Generated files:
DotNetWebApp.Models/Generated/Author.csDotNetWebApp.Models/Generated/Book.cs
make migrate
make devResult:
- ✅ REST API endpoints:
GET /api/authors,POST /api/books, etc. - ✅ UI: Click "Data" → "Author" or "Book" for CRUD pages
- ✅ Relationships: Book pages show Author name; Author pages list Books
Connection strings and API keys are stored in User Secrets (never in git):
# Set connection string
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;Database=DotNetWebApp;..."
# View all secrets
dotnet user-secrets list
# See SECRETS.md for details
cat SECRETS.md# Start SQL Server
make db-start# Regenerate schema from DDL and apply it
make run-ddl-pipeline
make migrate# Regenerate models
cd ModelGenerator
../dotnet-build.sh run ../app.yaml
cd ..
make build# Change port in launchSettings.json or run on different port
make dev # Uses ports from launchSettings.json| File | Purpose |
|---|---|
app.yaml |
📋 Generated data model (from SQL DDL) plus app metadata |
schema.sql |
📄 Source SQL DDL for the generation pipeline |
DotNetWebApp.Models/ |
🔄 Separate models assembly containing all data models |
DotNetWebApp.Models/Generated/ |
🔄 Auto-generated C# entities (don't edit directly) |
DotNetWebApp.Models/AppDictionary/ |
YAML model classes for app.yaml structure |
Migrations/ |
📚 Generated schema history (current baseline checked in; pipeline regenerates) |
seed.sql |
🧪 Seed data for the default schema (run after schema apply) |
DdlParser/ |
🆕 Converts SQL DDL → YAML |
ModelGenerator/ |
🔄 Converts YAML → C# entities |
SECRETS.md |
🔐 Connection string setup guide |
SESSION_SUMMARY.md |
📝 Documentation index |
SKILLS.md |
📚 Comprehensive developer skill guides |
- Parse your own database schema → See "Adding a New Data Entity from DDL" above
- Customize theme colors → Edit
app.yamltheme section - Add validation rules → Update
ModelGenerator/EntityTemplate.scriban(orapp.yamlmetadata) and regenerate - Create custom pages → Add
.razorfiles toComponents/Pages/ - Extend REST API → Add custom controllers in
Controllers/
- Backend: ASP.NET Core 8 Web API with Entity Framework Core
- Frontend: Blazor Server with Radzen UI components
- Database: SQL Server (Docker or native)
- Configuration: DDL-driven data models + JSON appsettings
- Model Generation: Automated from YAML via Scriban templates
- Modular Design: Models in separate
DotNetWebApp.Modelsassembly for better separation of concerns
dotnet-build.shmanages .NET SDK version conflicts; do not modify system .NET installDdlParserandModelGeneratorare part ofDotNetWebApp.sln; usemake run-ddl-pipelineto regenerate models/migrations- Generated entities use nullable reference types (
#nullable enable) - All value types for optional properties are nullable (
int?,decimal?, etc.) - Phase 1 Complete: See ARCHITECTURE_SUMMARY.md for detailed refactoring status and next steps
- For detailed implementation plans and architecture decisions, refer to CLAUDE.md
- See
SECRETS.mdfor connection string setup - See
CLAUDE.mdfor developer context - Review
SESSION_SUMMARY.mdfor current project state