Skip to content

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.

csharp
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class DbCommandAttribute : Attribute

Inheritance

objectAttributeDbCommandAttribute

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

csharp
[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

csharp
[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

csharp
[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)

csharp
[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 rows

Repository Integration Pattern

csharp
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

csharp
[DbCommand(sp: "archive_old_orders", dataSource: "ArchiveDatabase")]
public record ArchiveOldOrdersCommand(DateTime OlderThan) : ICommand<int>;

// Generated handler will resolve keyed service: [FromKeyedServices("ArchiveDatabase")] DbDataSource dataSource

Remarks

DbCommandAttribute Detailed Guide

Generated Code Behavior

This attribute triggers the source generator to create:

  1. ToDbParams() Extension Method: Converts class properties to Dapper-compatible parameter objects
  2. Command Handler Method: Static async method that executes the database command (when sp/sql/fn provided)

Command Handler Generation

Handlers are generated as static methods in a companion class (e.g., CreateUserCommandHandler.HandleAsync) that:

  • Accept the command object, DbDataSource, and CancellationToken
  • Open database connections automatically
  • Map parameters using the generated ToDbParams() method
  • Execute appropriate Dapper methods based on return type and nonQuery setting
  • Handle connection disposal and async patterns correctly

Parameter Mapping Rules

  • Record properties and primary constructor parameters are automatically mapped
  • Parameter names follow the paramsCase setting (None, SnakeCase, or global default)
  • Use [Column("custom_name")] attribute to override specific parameter names
  • MSBuild property DbCommandParamPrefix adds global prefixes to all parameters

Return Type Handling

  • ICommand<int/long>: Returns row count (ExecuteAsync) or scalar value (ExecuteScalarAsync)
  • ICommand<TResult>: Returns single object (QueryFirstOrDefaultAsync<TResult>)
  • ICommand<IEnumerable<TResult>>: Returns collection (QueryAsync<TResult>)
  • ICommand (no return type): Executes command without returning data (ExecuteAsync)

MSBuild Integration

Global configuration through MSBuild properties:

  • DbCommandDefaultParamCase: Sets default parameter case conversion (None, SnakeCase)
  • DbCommandParamPrefix: Adds prefix to all generated parameter names

Requirements

  • Target class must implement ICommand<TResult> or IQuery<TResult> (or parameterless versions)
  • Class must be partial if nested within another type
  • Only one of sp, sql, or fn can be specified per command
  • Assembly must reference Momentum.Extensions.SourceGenerators

Constructors

DbCommandAttribute(string?, string?, string?, DbParamsCase, bool, string?)

Marks a class for database command code generation, creating efficient data access patterns with minimal boilerplate.

csharp
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.

sql string?

The SQL query text to execute. Mutually exclusive with sp and fn.

fn string?

The database function name to call. Parameters are auto-generated from record properties. Mutually exclusive with sp and sql. 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

csharp
[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

csharp
[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

csharp
[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)

csharp
[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 rows

Repository Integration Pattern

csharp
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

csharp
[DbCommand(sp: "archive_old_orders", dataSource: "ArchiveDatabase")]
public record ArchiveOldOrdersCommand(DateTime OlderThan) : ICommand<int>;

// Generated handler will resolve keyed service: [FromKeyedServices("ArchiveDatabase")] DbDataSource dataSource

Remarks

DbCommandAttribute Detailed Guide

Generated Code Behavior

This attribute triggers the source generator to create:

  1. ToDbParams() Extension Method: Converts class properties to Dapper-compatible parameter objects
  2. Command Handler Method: Static async method that executes the database command (when sp/sql/fn provided)

Command Handler Generation

Handlers are generated as static methods in a companion class (e.g., CreateUserCommandHandler.HandleAsync) that:

  • Accept the command object, DbDataSource, and CancellationToken
  • Open database connections automatically
  • Map parameters using the generated ToDbParams() method
  • Execute appropriate Dapper methods based on return type and nonQuery setting
  • Handle connection disposal and async patterns correctly

Parameter Mapping Rules

  • Record properties and primary constructor parameters are automatically mapped
  • Parameter names follow the paramsCase setting (None, SnakeCase, or global default)
  • Use [Column("custom_name")] attribute to override specific parameter names
  • MSBuild property DbCommandParamPrefix adds global prefixes to all parameters

Return Type Handling

  • ICommand<int/long>: Returns row count (ExecuteAsync) or scalar value (ExecuteScalarAsync)
  • ICommand<TResult>: Returns single object (QueryFirstOrDefaultAsync<TResult>)
  • ICommand<IEnumerable<TResult>>: Returns collection (QueryAsync<TResult>)
  • ICommand (no return type): Executes command without returning data (ExecuteAsync)

MSBuild Integration

Global configuration through MSBuild properties:

  • DbCommandDefaultParamCase: Sets default parameter case conversion (None, SnakeCase)
  • DbCommandParamPrefix: Adds prefix to all generated parameter names

Requirements

  • Target class must implement ICommand<TResult> or IQuery<TResult> (or parameterless versions)
  • Class must be partial if nested within another type
  • Only one of sp, sql, or fn can be specified per command
  • Assembly must reference Momentum.Extensions.SourceGenerators

Properties

DataSource

Gets the data source key.

csharp
public string? DataSource { get; }

Property Value

string?

Fn

If set, a command handler will be generated using this function SQL query. Parameters will be automatically appended based on record properties.

csharp
public string? Fn { get; }

Property Value

string?

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).

csharp
public bool NonQuery { get; }

Property Value

bool

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.

csharp
public DbParamsCase ParamsCase { get; }

Property Value

DbParamsCase

Sp

If set, a command handler will be generated using this stored procedure.

csharp
public string? Sp { get; }

Property Value

string?

Sql

If set, a command handler will be generated using this SQL query.

csharp
public string? Sql { get; }

Property Value

string?