Class DbCommandAttribute
Namespace: Momentum.Extensions.Abstractions.Dapper
Assembly: Momentum.Extensions.Abstractions.dll
Marks a class for database command code generation, creating efficient data access patterns with minimal boilerplate.
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class DbCommandAttribute : AttributeInheritance
object ← Attribute ← DbCommandAttribute
Inherited Members
Attribute.GetCustomAttributes(MemberInfo, Type), Attribute.GetCustomAttributes(MemberInfo, Type, bool), Attribute.GetCustomAttributes(MemberInfo), Attribute.GetCustomAttributes(MemberInfo, bool), Attribute.IsDefined(MemberInfo, Type), Attribute.IsDefined(MemberInfo, Type, bool), Attribute.GetCustomAttribute(MemberInfo, Type), Attribute.GetCustomAttribute(MemberInfo, Type, bool), Attribute.GetCustomAttributes(ParameterInfo), Attribute.GetCustomAttributes(ParameterInfo, Type), Attribute.GetCustomAttributes(ParameterInfo, Type, bool), Attribute.GetCustomAttributes(ParameterInfo, bool), Attribute.IsDefined(ParameterInfo, Type), Attribute.IsDefined(ParameterInfo, Type, bool), Attribute.GetCustomAttribute(ParameterInfo, Type), Attribute.GetCustomAttribute(ParameterInfo, Type, bool), Attribute.GetCustomAttributes(Module, Type), Attribute.GetCustomAttributes(Module), Attribute.GetCustomAttributes(Module, bool), Attribute.GetCustomAttributes(Module, Type, bool), Attribute.IsDefined(Module, Type), Attribute.IsDefined(Module, Type, bool), Attribute.GetCustomAttribute(Module, Type), Attribute.GetCustomAttribute(Module, Type, bool), Attribute.GetCustomAttributes(Assembly, Type), Attribute.GetCustomAttributes(Assembly, Type, bool), Attribute.GetCustomAttributes(Assembly), Attribute.GetCustomAttributes(Assembly, bool), Attribute.IsDefined(Assembly, Type), Attribute.IsDefined(Assembly, Type, bool), Attribute.GetCustomAttribute(Assembly, Type), Attribute.GetCustomAttribute(Assembly, Type, bool), Attribute.Equals(object?), Attribute.GetHashCode(), Attribute.Match(object?), Attribute.IsDefaultAttribute(), Attribute.TypeId, object.GetType(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Examples
DbCommand Attribute Examples
Basic Stored Procedure Command
[DbCommand(sp: "create_user")]
public record CreateUserCommand(string Name, string Email) : ICommand<int>;
// Generated usage:
var command = new CreateUserCommand("John Doe", "john@example.com");
var userId = await CreateUserCommandHandler.HandleAsync(command, dataSource, cancellationToken);SQL Query with Custom Parameter Names
[DbCommand(sql: "SELECT * FROM users WHERE created_date >= @from_date AND status = @user_status",
paramsCase: DbParamsCase.SnakeCase)]
public record GetRecentUsersQuery(
[Column("from_date")] DateTime Since,
[Column("user_status")] string Status) : IQuery<IEnumerable<User>>;
// Generated ToDbParams() creates: { from_date = Since, user_status = Status }Database Function Call
[DbCommand(fn: "$get_user_orders")]
public record GetUserOrdersQuery(int UserId, bool IncludeInactive = false) : IQuery<IEnumerable<Order>>;
// Generated SQL: "SELECT * FROM get_user_orders(@UserId, @IncludeInactive)"Non-Query Command (INSERT/UPDATE/DELETE)
[DbCommand(sql: "UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = @UserId", nonQuery: true)]
public record UpdateLastLoginCommand(int UserId) : ICommand<int>;
// Returns number of affected rowsRepository Integration Pattern
public class UserRepository
{
private readonly DbDataSource _dataSource;
public UserRepository(DbDataSource dataSource) => _dataSource = dataSource;
public async Task<int> CreateUserAsync(string name, string email, CancellationToken ct = default)
{
var command = new CreateUserCommand(name, email);
return await CreateUserCommandHandler.HandleAsync(command, _dataSource, ct);
}
public async Task<IEnumerable<User>> GetRecentUsersAsync(DateTime since, string status, CancellationToken ct = default)
{
var query = new GetRecentUsersQuery(since, status);
return await GetRecentUsersQueryHandler.HandleAsync(query, _dataSource, ct);
}
}Dependency Injection with Named Data Sources
[DbCommand(sp: "archive_old_orders", dataSource: "ArchiveDatabase")]
public record ArchiveOldOrdersCommand(DateTime OlderThan) : ICommand<int>;
// Generated handler will resolve keyed service: [FromKeyedServices("ArchiveDatabase")] DbDataSource dataSourceRemarks
Important: Only one of sp, sql, or fn should be specified. If multiple are provided, the source generator will emit a diagnostic error.
Constructors
DbCommandAttribute(string?, string?, string?, DbParamsCase, bool, string?)
Marks a class for database command code generation, creating efficient data access patterns with minimal boilerplate.
public DbCommandAttribute(string? sp = null, string? sql = null, string? fn = null, DbParamsCase paramsCase = DbParamsCase.Unset, bool nonQuery = false, string? dataSource = null)Parameters
sp string?
The name of the stored procedure to execute. Mutually exclusive with sql and fn. Only one of these three parameters may be specified.
sql string?
The SQL query text to execute. Mutually exclusive with sp and fn. Only one of these three parameters may be specified.
fn string?
The database function name to call. Parameters are auto-generated from record properties. Mutually exclusive with sp and sql. Only one of these three parameters may be specified. Use '$' prefix (e.g., "$get_user_orders") to generate "SELECT * FROM get_user_orders(...)" syntax.
paramsCase DbParamsCase
Specifies how property names are converted to database parameter names. Defaults to global MSBuild configuration.
nonQuery bool
Controls the execution strategy for database commands. Default is false.
If true: Uses Dapper's ExecuteAsync() for commands returning row counts (INSERT/UPDATE/DELETE operations). If false: Uses appropriate query methods (QueryAsync, QueryFirstOrDefaultAsync, ExecuteScalarAsync) based on the return type.
dataSource string?
The keyed data source name for dependency injection. If null, uses the default registered data source.
Examples
DbCommand Attribute Examples
Basic Stored Procedure Command
[DbCommand(sp: "create_user")]
public record CreateUserCommand(string Name, string Email) : ICommand<int>;
// Generated usage:
var command = new CreateUserCommand("John Doe", "john@example.com");
var userId = await CreateUserCommandHandler.HandleAsync(command, dataSource, cancellationToken);SQL Query with Custom Parameter Names
[DbCommand(sql: "SELECT * FROM users WHERE created_date >= @from_date AND status = @user_status",
paramsCase: DbParamsCase.SnakeCase)]
public record GetRecentUsersQuery(
[Column("from_date")] DateTime Since,
[Column("user_status")] string Status) : IQuery<IEnumerable<User>>;
// Generated ToDbParams() creates: { from_date = Since, user_status = Status }Database Function Call
[DbCommand(fn: "$get_user_orders")]
public record GetUserOrdersQuery(int UserId, bool IncludeInactive = false) : IQuery<IEnumerable<Order>>;
// Generated SQL: "SELECT * FROM get_user_orders(@UserId, @IncludeInactive)"Non-Query Command (INSERT/UPDATE/DELETE)
[DbCommand(sql: "UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = @UserId", nonQuery: true)]
public record UpdateLastLoginCommand(int UserId) : ICommand<int>;
// Returns number of affected rowsRepository Integration Pattern
public class UserRepository
{
private readonly DbDataSource _dataSource;
public UserRepository(DbDataSource dataSource) => _dataSource = dataSource;
public async Task<int> CreateUserAsync(string name, string email, CancellationToken ct = default)
{
var command = new CreateUserCommand(name, email);
return await CreateUserCommandHandler.HandleAsync(command, _dataSource, ct);
}
public async Task<IEnumerable<User>> GetRecentUsersAsync(DateTime since, string status, CancellationToken ct = default)
{
var query = new GetRecentUsersQuery(since, status);
return await GetRecentUsersQueryHandler.HandleAsync(query, _dataSource, ct);
}
}Dependency Injection with Named Data Sources
[DbCommand(sp: "archive_old_orders", dataSource: "ArchiveDatabase")]
public record ArchiveOldOrdersCommand(DateTime OlderThan) : ICommand<int>;
// Generated handler will resolve keyed service: [FromKeyedServices("ArchiveDatabase")] DbDataSource dataSourceRemarks
Important: Only one of sp, sql, or fn should be specified. If multiple are provided, the source generator will emit a diagnostic error.
Properties
DataSource
Gets the data source key.
public string? DataSource { get; }Property Value
Fn
If set, a command handler will be generated using this function SQL query. Parameters will be automatically appended based on record properties.
public string? Fn { get; }Property Value
NonQuery
Indicates the nature of the command. This flag primarily influences behavior for ICommand<int/long>.
If true:
- For ICommand<int/long>: The generated handler will use Dapper's ExecuteAsync (expecting rows affected).
- For ICommand<TResult> where TResult is not int: A warning will be issued by the source generator, as using NonQuery=true with a command expecting a specific data structure is atypical. The handler will default to execute a Query or QueryFirstOrDefault call and return default(TResult).
If false:
- For ICommand<int>: The generated handler will use Dapper's ExecuteScalarAsync<int> (expecting a scalar integer query result).
- For ICommand<TResult> where TResult is not int: The handler will perform a query (e.g., QueryFirstOrDefault or Query).
public bool NonQuery { get; }Property Value
ParamsCase
Specifies how property names are converted to database parameter names in the generated ToDbParams() method.
- : Uses the global default specified by the DbCommandDefaultParamCase MSBuild property
- : Uses property names as-is without any conversion
- : Converts property names to snake_case (e.g., FirstName -> first_name)
Individual properties can override this behavior using the [Column("custom_name")] attribute.
public DbParamsCase ParamsCase { get; }Property Value
Sp
If set, a command handler will be generated using this stored procedure.
public string? Sp { get; }Property Value
Sql
If set, a command handler will be generated using this SQL query.
public string? Sql { get; }