⚠️ Preview Release: RoomSharp is currently in Preview. Learn more →

Generated Code

Understanding what RoomSharp's source generator creates

How Code Generation Works

RoomSharp uses C# Source Generators to create implementations at compile-time. When you mark interfaces with [Dao] and classes with [Database], the generator analyzes your code and produces concrete implementations.

What Gets Generated

1. DAO Implementations

For each [Dao] interface, RoomSharp generates a concrete class with Impl suffix:

C# - Your Interface
[Dao]
public interface IUserDao
{
    [Insert]
    long Insert(User user);
    
    [Query("SELECT * FROM users WHERE Id = :id")]
    Task<User?> GetByIdAsync(long id);
}
C# - Generated Implementation
public class IUserDaoImpl : IUserDao
{
    private readonly RoomDatabase _database;
    private readonly ILogger? _logger;

    public IUserDaoImpl(RoomDatabase database, ILogger? logger)
    {
        _database = database;
        _logger = logger;
    }

    public long Insert(User user)
    {
        var connection = _database.GetConnection();
        using var command = connection.CreateCommand();
        command.CommandText = "INSERT INTO users (Email, Name) VALUES (@p0, @p1)";
        command.Parameters.Add(CreateParameter("@p0", user.Email));
        command.Parameters.Add(CreateParameter("@p1", user.Name));
        command.ExecuteNonQuery();
        return connection.LastInsertedRowId;
    }

    public async Task<User?> GetByIdAsync(long id)
    {
        var connection = _database.GetConnection();
        using var command = connection.CreateCommand();
        command.CommandText = "SELECT * FROM users WHERE Id = @p0";
        command.Parameters.Add(CreateParameter("@p0", id));
        
        using var reader = await command.ExecuteReaderAsync();
        if (await reader.ReadAsync())
        {
            return MapUser(reader);
        }
        return null;
    }

    private User MapUser(DbDataReader reader)
    {
        // IL-based high-performance mapping
        return ILFactory.MapUser(reader);
    }
}

2. Database Implementations

For [Database] classes, RoomSharp generates the Impl version:

C# - Your Database Class
[Database(Version = 1, Entities = [typeof(User)])]
public abstract class AppDatabase : RoomDatabase
{
    protected AppDatabase(IDatabaseProvider provider, ILogger? logger = null) 
        : base(provider, logger) { }
    
    public abstract IUserDao UserDao { get; }
}
C# - Generated Implementation
public class AppDatabaseImpl : AppDatabase
{
    private IUserDao? _userDao;

    public AppDatabaseImpl(IDatabaseProvider provider, ILogger? logger = null)
        : base(provider, logger)
    {
    }

    public override IUserDao UserDao
    {
        get
        {
            if (_userDao == null)
            {
                _userDao = new IUserDaoImpl(this, Logger);
            }
            return _userDao;
        }
    }
}

3. IL-Based Mappers

RoomSharp generates high-performance IL code for entity mapping using ILFactory:

  • Zero boxing for value types
  • Direct property setters (no reflection)
  • Span-based fast paths
  • Type-safe reading with ReaderKind

Generated Files Location

Generated files appear in your project's obj/ folder:

File Structure
YourProject/
└── obj/
    └── Debug/
        └── net8.0/
            └── generated/
                └── RoomSharp.SourceGenerator/
                    ├── IUserDaoImpl.g.cs
                    ├── AppDatabaseImpl.g.cs
                    └── EntityMappers.g.cs

Viewing Generated Code

To view generated code in Visual Studio or Rider:

  1. Expand the project in Solution Explorer
  2. Expand "Dependencies" → "Analyzers" → "RoomSharp.SourceGenerator"
  3. View the generated .g.cs files
💡 Tip: Generated files are regenerated on every build. Don't edit them manually - changes will be lost!

Compile-Time Validation

The source generator performs validation during compilation:

  • Ensures [PrimaryKey] is defined on entities
  • Validates SQL syntax in [Query] attributes
  • Checks parameter names match method parameters
  • Verifies return types are compatible with operations
  • Detects missing or duplicate column mappings

Performance Optimizations in Generated Code

Zero Allocations in Hot Loops

Batch operations use pre-allocated buffers and reuse prepared statements:

C# - Generated Batch Insert
public void InsertAll(List<User> users)
{
    var engine = new BatchInsertEngine(
        _database,
        tableName: "users",
        columnNames: s_columnNames, // static readonly ImmutableArray
        bindValues: BindUserValues    // static method, zero closure
    );
    engine.ExecuteBatch(users);
}

Static Column Arrays

Column names are stored as static readonly ImmutableArray for reuse:

C#
private static readonly ImmutableArray<string> s_columnNames = 
    ImmutableArray.Create("Id", "Email", "Name");

Transaction Wrapping

Methods with [Transaction] are wrapped automatically:

C# - Generated Transaction Code
public async Task<long> UpsertAsync(User user)
{
    return await _database.RunInTransactionAsync(async () =>
    {
        // Your method body here
        var existing = await FindByEmailAsync(user.Email);
        if (existing is null)
            return Insert(user);
        
        existing.Name = user.Name;
        Update(existing);
        return existing.Id;
    });
}

Debugging Generated Code

If you encounter issues with generated code:

  1. Check build output for generator warnings/errors
  2. View the generated files in the analyzers section
  3. Enable detailed generator logging in your project file:
    XML - .csproj
    <PropertyGroup>
      <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
      <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
    </PropertyGroup>
  4. Clean and rebuild the solution

Next Steps