Parameter binding
Convert an application value
public readonly record struct Names(IReadOnlyList<string> Items);
sealed class NamesParamInfo : ConvertedDbParamInfo<Names>
{
protected override object ConvertValue(Names value) => string.Join(',', value.Items);
protected override void ConfigureParameter(IDbDataParameter parameter)
=> parameter.DbType = DbType.String;
}
The command can hold that parameter strategy.
static readonly QueryCommand SaveSearch = new("INSERT INTO saved_searches (Names) VALUES (@names)");
SaveSearch.UpdateParamCache("@names", new NamesParamInfo());
SaveSearch.Execute(cnn, new { names = new Names(["Blue", "Live"]) });
// @names contains Blue,Live.
Learn metadata from an existing command
static readonly QueryCommand UpdateAlbum = new("UPDATE albums SET Title = @title WHERE AlbumId = @albumId");
using DbCommand providerCommand = cnn.CreateCommand();
providerCommand.CommandText = "UPDATE albums SET Title = @title WHERE AlbumId = @albumId";
DbParameter albumId = providerCommand.CreateParameter();
albumId.ParameterName = "@albumId";
albumId.DbType = DbType.Int32;
providerCommand.Parameters.Add(albumId);
DbParameter title = providerCommand.CreateParameter();
title.ParameterName = "@title";
title.DbType = DbType.String;
title.Size = 200;
providerCommand.Parameters.Add(title);
UpdateAlbum.UpdateCache(providerCommand);
UpdateCache reads parameter metadata through the registered parameter metadata getters.
A provider can add another metadata reader through IDbParamInfoGetter.ParamGetterMakers.
Change exposed parameter names
public sealed class EmployeeArgs
{
[ParameterName("EmployeeName")]
[ParameterAlias("NameForSearch")]
public string? Name { get; init; }
[ParameterIgnore]
public string? DebugNote { get; init; }
}
Flatten another parameter object
public sealed class UpdateArgs
{
public int EmployeeId { get; init; }
[NestedParameters("Employee")]
public EmployeeArgs Employee { get; init; } = new();
}
static readonly QueryCommand UpdateEmployee = new("UPDATE employees SET Name = @EmployeeName WHERE EmployeeId = @EmployeeId");
UpdateEmployee.Execute(cnn, new UpdateArgs
{
EmployeeId = 12,
Employee = new EmployeeArgs { Name = "Ana" }
});
Same priority conflict
public sealed class SearchTermA
{
public string? Value { get; init; }
}
public sealed class SearchTermB
{
public string? Value { get; init; }
}
[ParameterConflict(ParameterConflictBehavior.TakeOne)]
public sealed class SearchArgs
{
[NestedParameters]
public SearchTermA Primary { get; init; } = new();
[NestedParameters]
public SearchTermB Secondary { get; init; } = new();
}
var search = new QueryCommand("SELECT @Value").StartBuilder();
search.UseWith(new SearchArgs
{
Primary = new() { Value = "blue" },
Secondary = new() { Value = "green" }
});
object? value = search["@Value"];
// TakeOne allows either value at the same priority.
A direct member has priority over a flattened member.
Provider metadata reader
IDbParamInfoGetter.ParamGetterMakers contains the provider metadata readers tried for a command.
DbParameterDefaults supplies the application wide fallback when no provider reader claims the command. Its contract is IDbParameterDefaults.
Nested custom parameter access
PathAccessorEmitterBase keeps a custom member rule path aware when the member is reached through NestedParameters. The lower level contract is IPathAccessorEmitter.
Command specific binding remains on DbParamInfo.