/DB

Project structure

Recommended layout for a project that uses Socigy.OpenSource.DB, covering the DB class library, SQL procedure files, and generated output.

updated 5 Jun 20263 min readv0.3.2View as Markdown

There are no hard requirements on directory structure, but isolating DB models in a dedicated class library is strongly recommended. It lets multiple consumers (API, background workers, test projects) reference the same typed models without pulling in unrelated dependencies.

MyApp.sln
MyApp.sln
├── MyApp.DB/ <- class library for DB models
│ ├── MyApp.DB.csproj
│ ├── socigy.json <- migration and codegen config (project root)
│ ├──
│ │ ├── User.cs <- [Table] partial class
│ │ └── UserRole.cs <- [Flags] enum mapped with [Table]
│ ├──
│ │ └── Product.cs
│ └──
│ └── Procedures/ <- .sql files for procedure mapping
│ └── GetUserById.sql
└──
└── MyApp.API.csproj

The DB project file

The DB class library references Socigy.OpenSource.DB. If you use Procedure mapping, declare .sql files as <AdditionalFiles> so the source generator can read them at build time:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Socigy.OpenSource.DB" Version="0.3.2" />
  </ItemGroup>

  <!-- Only needed for procedure mapping -->
  <ItemGroup>
    <AdditionalFiles Include="Socigy\Procedures\**\*.sql" />
  </ItemGroup>

  <!-- Optionally emit generated files into the project tree for inspection -->
  <PropertyGroup>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
  </PropertyGroup>
</Project>
NOTE
The AdditionalFiles glob is required only if you use procedure mapping. Omit it for projects that use table-based queries alone.
TIP
Setting <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> writes generated files into $(IntermediateOutputPath)/generated/ so IDEs can index them more reliably. It is optional.

Model class requirements

Every class that maps to a database table must meet two conditions:

  1. Decorated with [Table("table_name")], which tells the generator the SQL table this class represents.
  2. Declared partial, which lets the generator add members (query methods, IDbTable implementation, column constants) in a companion generated file.
using Socigy.OpenSource.DB.Attributes;

// Correct: the generator augments this class
[Table("products")]
public partial class Product { /* ... */ }

// Wrong: the generator skips this class, no query methods are emitted
[Table("products")]
public class Product { /* ... */ }

The class can live in any namespace. Nesting inside another class is not supported.

NOTE
Schema attributes and the DbDefaults constants resolve from Socigy.OpenSource.DB.Attributes. The only attribute outside that namespace is [Nullable], in Socigy.OpenSource.DB.Core.Attributes.

Generated files

The source generator writes one .g.cs file per annotated class under the intermediate output path:

obj/{Configuration}/net{version}/generated/Socigy.OpenSource.DB.SourceGenerator/Socigy.OpenSource.DB.SourceGenerator.Program/

For example, a Debug build targeting net10.0 uses:

obj/Debug/net10.0/generated/Socigy.OpenSource.DB.SourceGenerator/Socigy.OpenSource.DB.SourceGenerator.Program/

To have the IDE index these files reliably, add <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> to your .csproj.

Never edit these files directly, they are recreated on every build. Each generated file contains:

  • The IDbTable interface implementation (GetTableName(), GetColumns(), GetPrimaryColumns(), GetColumn(), GetDbColumnName()).
  • Static factory methods: Query(), Insert(), Update(), Delete().
  • Instance shortcut methods: user.Insert(), user.Update(), user.Delete().
  • Column name constants (see below).

When a databaseName is configured, the generator also emits the database context (IAppDb / AppDbContext), the per-table sets, and the DI extensions. See Database context.


Static column name constants

The generator emits a {PropertyName}ColumnName constant on each model class for every mapped property, using the snake_case database column name:

User.UsernameColumnName   // => "username"
User.CreatedAtColumnName  // => "created_at"
User.IdColumnName         // => "id"

These help when building dynamic SQL fragments or logging query details without hard-coding string literals.


Keeping obj/ out of source control

Generated files should never be committed. Add obj/ to your .gitignore:

obj/
bin/

The generator recreates all output on the next dotnet build, so nothing is lost by excluding it.