Appearance
Carrot.Data.Scaffold.Process.Database
net
Database scaffolding process -- discovers entity definitions from an assembly and builds a complete database graph (entities, contexts, tables, navigations, keys, indices, conversions) for EF Core code generation.
Installation
Project reference:
xml
<ProjectReference Include="..\Carrot.Data.Scaffold.Process.Database\Carrot.Data.Scaffold.Process.Database.csproj" />Architecture
Process & Concepts
ScaffoldDatabaseProcess extends ScaffoldProcess<ScaffoldDatabase, ScaffoldDatabaseOptions> and registers 7 concepts:
| Concept | Flavor | Key | Graph Type |
|---|---|---|---|
| Database | DeclarativeSingle | (singleton) | ScaffoldDatabaseGraphDatabase |
| Contexts | Declarative | string | ScaffoldDatabaseGraphContext |
| Entities | Introspective | Type | ScaffoldDatabaseGraphEntity |
| Enums | Introspective | Type | ScaffoldDatabaseGraphEnum |
| Abstract | IntrospectiveAbstract | Type | ScaffoldDatabaseGraphEntity |
| Traits | Introspective | Type | ScaffoldDatabaseGraphTrait |
| TraitBehaviours | Declarative | string | ScaffoldDatabaseGraphTraitBehaviour |
A built-in "Full" context (ScaffoldDatabaseDefineContextFull) is always registered.
Entity Configuration
The configure API mirrors EF Core conventions with expression-based property selectors:
HasPrimaryKey(x => x.Id)-- primary key with generation strategyHasAlternateKey(x => x.Slug)-- unique alternate keysHasIndex(x => x.Email)-- indices with uniqueness, filtersProperty<T>(x => x.Name)-- per-property column configNavigationTo<TTo>(x => x.Parent)-- FK navigations with cardinality, delete behaviorNavigationLinkTable<TA, TB>(x => x.Tags, x => x.Items)-- many-to-many link tablesConfigureTable(t => t.Schema("dbo"))-- table naming, schema overrides
Navigation System
The navigation graph is rich, supporting:
- Direct FK navigations with return navigations
- Link table (many-to-many) navigations with both sides modeled
- Navigation shortcuts -- multi-hop paths through the entity graph
- Cardinality tracking (one-to-one, one-to-many, many-to-many)
- Delete behavior configuration
Trait System
Traits define reusable property and navigation sets. ScaffoldDatabaseGraphTrait can carry properties, primary keys, navigations, and foreign keys that get applied to entities via trait behaviours.
Graph Builder & Provider Pipeline
Graph builders (ScaffoldDatabaseGraphEntityBuilder, etc.) handle graph construction. Providers (ScaffoldGraphEntityProvider, ScaffoldGraphTableProvider, etc.) handle the decomposition of entities into tables, columns, foreign keys, indices, etc.
Naming Policy
IScaffoldDatabaseNamingPolicy controls all generated database identifiers (table names, column names, FK names, index names, etc.). The default policy supports multiple casing strategies, truncation styles, and provider-specific naming.
Disclosure System
IScaffoldDatabaseDiscloseable -- entities, properties, contexts, and other graph items support a disclosure system for controlling visibility/accessibility in generated code.
Dependencies
Carrot.Data.Scaffold.Process-- core scaffolding framework- .NET 10
Build
bash
dotnet buildUsage Guide
Getting Started
csharp
using Carrot.Data.Scaffold;
using Carrot.Data.Scaffold.Process;
var process = new ScaffoldDatabaseProcess(
typeof(MyAssemblyMarker).Assembly,
new ScaffoldDatabaseOptions(),
log);
ScaffoldDatabase result = process.Scaffold();
// result.Entities, result.Contexts, result.Database, etc.Common Patterns
Defining a Database
csharp
public class MyDatabase : ScaffoldDatabaseDefineDatabase
{
public override string DatabaseName => "MyPlatform";
protected override void ScaffoldImpl(ScaffoldDatabaseConfigureDatabase config, ILog? log)
{
// Database-level configuration
}
}Defining a Context
csharp
public class TenantContext : ScaffoldDatabaseDefineContext
{
public override string ContextName => "Tenant";
public override void Scaffold(ScaffoldDatabaseConfigureContext config, ILog? log = null)
{
// Context-specific entity filtering
}
}Defining an Entity
csharp
public class UserScaffold : ScaffoldDatabaseDefineEntity<User>
{
public override void Scaffold(
ScaffoldDatabaseConfigureEntity<User> config,
ScaffoldDatabaseReflectedEntity entity,
ILog? log)
{
config.HasPrimaryKey(x => x.Id);
config.HasAlternateKey(x => x.Email);
config.HasIndex(x => x.CreatedAt);
config.Property<string>(x => x.Email, p =>
{
// Column-level configuration
});
config.Property<string>(x => x.DisplayName);
config.NavigationTo<Organisation>(x => x.Organisation);
config.ConfigureTable(t =>
{
// Table schema, naming overrides
});
}
}Many-to-Many Link Tables
csharp
public class UserRoleScaffold : ScaffoldDatabaseDefineEntity<UserRole>
{
public override void Scaffold(
ScaffoldDatabaseConfigureEntity<UserRole> config,
ScaffoldDatabaseReflectedEntity entity,
ILog? log)
{
config.HasPrimaryKey(x => x.Id);
config.NavigationLinkTable<User, Role>(
x => x.User,
x => x.Role);
}
}Abstract Base Entity
Apply shared configuration to all entities inheriting from a base type:
csharp
public class PlatformEntityScaffold : ScaffoldDatabaseDefineAbstract<PlatformEntity>
{
public override void Scaffold(
ScaffoldDatabaseConfigureEntity<PlatformEntity> config,
ScaffoldDatabaseReflectedEntity entity,
ILog? log)
{
config.HasPrimaryKey(x => x.Id);
config.Property<DateTimeOffset>(x => x.CreatedAt);
config.Property<DateTimeOffset>(x => x.UpdatedAt);
}
}Defining an Enum
csharp
public class UserStatusScaffold : ScaffoldDatabaseDefineEnum<UserStatus>
{
public override void Scaffold(ScaffoldDatabaseConfigureEnum config, ILog? log = null)
{
// Enum values auto-discovered; configure overrides here
}
}Defining a Trait
Traits let you define reusable property/navigation sets applied across entities:
csharp
public class AuditableScaffold : ScaffoldDatabaseDefineTrait<IAuditable>
{
public override void Scaffold(ScaffoldDatabaseConfigureTrait config, ILog? log = null)
{
config.Property<DateTimeOffset>("CreatedAt");
config.Property<DateTimeOffset>("UpdatedAt");
config.Property<string>("CreatedBy");
}
}Accessing Results
csharp
ScaffoldDatabase db = process.Scaffold();
// Iterate entities
foreach (var entity in db.Entities.Values)
{
// entity.Properties, entity.Navigations, entity.PrimaryKey, etc.
}
// Get contexts (excluding the built-in "Full" context)
foreach (var context in db.GetContexts(includeFullContext: false))
{
// context-specific entity sets
}
// Access the database singleton
var database = db.Database.Value;Tips / Gotchas
- A "Full" context is always auto-registered. Use
GetContexts(includeFullContext: false)to exclude it when iterating. RegisterAllConversionsdefaults to true inScaffoldDatabaseOptions. Set to false if you only want explicitly defined conversions.- Expression selectors resolve property names from the lambda. Multi-property keys use
params Expression[]overloads. - Abstract entity scaffolds run at priority 1 -- they execute before concrete entities, so base configuration is established first.
- Navigation cardinality is inferred from the property type (single reference = one-to-one/many-to-one, collection = one-to-many).
- The naming policy controls all identifiers. Swap
IScaffoldDatabaseNamingPolicyto customize table names, FK conventions, index naming, etc.