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:
[Dao]
public interface IUserDao
{
[Insert]
long Insert(User user);
[Query("SELECT * FROM users WHERE Id = :id")]
Task<User?> GetByIdAsync(long id);
}
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:
[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; }
}
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:
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:
- Expand the project in Solution Explorer
- Expand "Dependencies" → "Analyzers" → "RoomSharp.SourceGenerator"
- View the generated
.g.csfiles
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:
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:
private static readonly ImmutableArray<string> s_columnNames =
ImmutableArray.Create("Id", "Email", "Name");
Transaction Wrapping
Methods with [Transaction] are wrapped automatically:
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:
- Check build output for generator warnings/errors
- View the generated files in the analyzers section
- Enable detailed generator logging in your project file:
XML - .csproj
<PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath> </PropertyGroup> - Clean and rebuild the solution
Next Steps
- DAO Interfaces - Learn what gets generated for different attributes
- Performance Notes - Understand IL-based optimizations
- Error Handling - Troubleshoot generator issues

