9.5 KiB
ADO.NET Extensions Reference
DynamORM exposes extension helpers mainly in:
DynamORM/DynamicExtensions.csDynamORM/Helpers/DataReaderExtensions.csDynamORM/Helpers/ReaderExtensions.cs
IDbCommand Extensions
Connection and Transaction
SetConnection(this IDbCommand, IDbConnection)SetTransaction(this IDbCommand, IDbTransaction)
These enable fluent command setup.
SetCommand Overloads
Core overloads:
SetCommand(CommandType, int timeout, string text, params object[] args)SetCommand(int timeout, string text, params object[] args)SetCommand(CommandType, string text, params object[] args)SetCommand(string text, params object[] args)SetCommand(IDynamicQueryBuilder builder)
Examples:
cmd.SetCommand("SELECT * FROM users WHERE id = {0}", 19);
cmd.SetCommand(CommandType.StoredProcedure, "sp_DoWork");
cmd.SetCommand(builder);
Parameter Helpers
Bulk:
AddParameters(DynamicDatabase, params object[] args)AddParameters(DynamicDatabase, ExpandoObject)AddParameters(DynamicDatabase, DynamicExpando)
Single parameter helpers:
AddParameter(DynamicDatabase, object item)AddParameter(DynamicDatabase, string name, object item)AddParameter(IDynamicQueryBuilder, DynamicSchemaColumn? col, object value)AddParameter(IDynamicQueryBuilder, DynamicColumn item)
Advanced overloads support explicit ParameterDirection, DbType, size, precision, scale.
Compatibility note on overload resolution:
- Best practice is to cast value arguments to
objectwhen callingAddParameter(...)with value payloads. - Alternatively, use named arguments to force the intended overload.
- This avoids accidental binding to a different overload in ambiguous cases.
- Current overload shape is preserved for backward compatibility.
Example:
cmd.AddParameter("@Result", ParameterDirection.Output, DbType.String, 256, 0, 0, DBNull.Value);
cmd.AddParameter("@Name", DbType.String, 50, "Alice");
Updating Existing Parameters
SetParameter(this IDbCommand, string parameterName, object value)SetParameter(this IDbCommand, int index, object value)
Execution and Conversion
ExecuteScalarAs<T>()ExecuteScalarAs<T>(defaultValue)ExecuteScalarAs<T>(TryParseHandler<T>)ExecuteScalarAs<T>(defaultValue, TryParseHandler<T>)ExecuteEnumeratorOf<T>(defaultValue, TryParseHandler<T>)
These convert ADO.NET results to requested types with fallback behavior.
Command Debugging
DumpToString()Dump(StringBuilder)Dump(TextWriter)
Useful with DynamicDatabase.DumpCommands and custom log sinks.
IDataReader and Row Helpers
From DynamicExtensions.cs:
ToList(this IDataReader)RowToDynamic(this IDataReader)RowToExpando(this IDataReader)RowToDynamic(this DataRow)RowToExpando(this DataRow)- upper-case variants (
RowToDynamicUpper,RowToExpandoUpper) GetFieldDbType(this IDataReader, int i)
Reader Caching
CachedReader(this IDataReader, int offset = 0, int limit = -1, Func<DynamicCachedReader, int, bool> progress = null)
This creates an in-memory DynamicCachedReader snapshot.
DataReaderExtensions
ToDataTable(this IDataReader, string name = null, string nameSpace = null)
Converts current reader rows/schema to a DataTable.
ReaderExtensions Null-Safe Accessors
Typed nullable access by column name:
GetBooleanIfNotNullGetByteIfNotNullGetCharIfNotNullGetDateTimeIfNotNullGetDecimalIfNotNullGetDoubleIfNotNullGetFloatIfNotNullGetGuidIfNotNullGetInt16IfNotNullGetInt32IfNotNullGetInt64IfNotNullGetStringIfNotNullGetValueIfNotNull
Each method accepts an optional default value and returns it when DB value is null.
DynamicCachedReader
DynamicCachedReader implements IDataReader and stores:
- schema metadata
- field names/types/ordinals
- full row cache (supports multi-result sets)
Construction options:
new DynamicCachedReader(IDataReader, offset, limit, progress)DynamicCachedReader.FromDynamicEnumerable(...)DynamicCachedReader.FromEnumerable<T>(...)DynamicCachedReader.FromEnumerable(Type, IEnumerable)
Typical usage:
using (var rdr = cmd.ExecuteReader())
using (var cached = rdr.CachedReader())
{
while (cached.Read())
{
var row = cached.RowToDynamic();
}
}
When to use it:
- You need disconnected reader semantics.
- You need multiple passes or deferred mapping.
- You need stable materialization before connection disposal.
Tradeoff:
- Higher memory use proportional to result size.
End-to-End Samples (Genericized)
These samples mirror real production patterns while using generic names.
Sample 1: Stored Procedure with Multiple Result Sets + Null-Safe Reader Accessors
var records = new Dictionary<string, GenericMessage>();
using var con = Database.Open();
using var cmd = con.CreateCommand()
.SetCommand(CommandType.StoredProcedure, "usp_get_messages");
using var r = cmd.ExecuteReader();
while (r.Read())
{
var msg = new GenericMessage
{
Id = r.GetStringIfNotNull("id"),
Type = r.GetStringIfNotNull("type"),
Value = r.GetStringIfNotNull("value"),
State = (GenericMessageState)(r.GetInt32IfNotNull("state") ?? 0),
Date = r.GetDateTimeIfNotNull("date") ?? new DateTime(2099, 12, 31),
};
records.Add(msg.Id, msg);
}
if (r.NextResult())
{
while (r.Read())
{
var messageId = r.GetStringIfNotNull("message_id");
if (!records.ContainsKey(messageId))
continue;
records[messageId].Items.Add(new GenericMessageItem
{
Id = r.GetStringIfNotNull("id"),
Type = r.GetStringIfNotNull("type"),
Value = r.GetStringIfNotNull("value"),
});
}
}
Extension methods used:
SetCommand(CommandType, string)GetStringIfNotNullGetInt32IfNotNullGetDateTimeIfNotNull
Sample 2: Stored Procedure with Input/Output Parameters + Result Check
using var con = Database.Open();
using var cmd = con.CreateCommand()
.SetCommand(CommandType.StoredProcedure, "usp_set_message_status")
.AddParameter(Database.GetParameterName("id"), DbType.String, (object)messageId)
.AddParameter(Database.GetParameterName("status"), DbType.Int32, (object)(int)status)
.AddParameter(Database.GetParameterName("note"), DbType.String, (object)note)
.AddParameter(Database.GetParameterName("result"), ParameterDirection.Output, DbType.Int32, 0, null)
.AddParameter(Database.GetParameterName("result_description"), ParameterDirection.Output, DbType.String, 1024, null);
cmd.ExecuteNonQuery();
var result = cmd.Parameters[Database.GetParameterName("result")] as IDataParameter;
var resultDescription = cmd.Parameters[Database.GetParameterName("result_description")] as IDataParameter;
if (result != null && result.Value != DBNull.Value && result.Value != null)
{
if ((int)result.Value != 0)
{
var description = (resultDescription != null &&
resultDescription.Value != DBNull.Value &&
resultDescription.Value != null)
? resultDescription.Value.ToString()
: "UNSPECIFIED";
throw new Exception($"{result.Value} - {description}");
}
}
Extension methods used:
SetCommand(CommandType, string)AddParameter(...)overloads withDbTypeandParameterDirectionDumpToString()(optional diagnostic logging)
Sample 3: Parameterized UPDATE with Runtime SQL and ADO.NET Extensions
using var conn = Database.Open();
using var cmdUpdate = conn.CreateCommand()
.SetCommand($@"UPDATE {Database.DecorateName("transport_task")}
SET {Database.DecorateName("confirmed")} = {Database.GetParameterName("confirmed")}
WHERE {Database.DecorateName("id")} = {Database.GetParameterName("id")}")
.AddParameter(Database.GetParameterName("confirmed"), DbType.Int32, (object)1)
.AddParameter(Database.GetParameterName("id"), DbType.Guid, (object)taskId);
if (cmdUpdate.ExecuteNonQuery() > 0)
{
// updated
}
Extension methods used:
SetCommand(string, params object[])AddParameter(string, DbType, object)
Sample 4: Parameterized SELECT + Reader Mapping Method
using var conn = Database.Open();
using var cmd = conn.CreateCommand()
.SetCommand($@"SELECT * FROM {Database.DecorateName("transport_task")}
WHERE {Database.DecorateName("id")} = {Database.GetParameterName("id")}")
.AddParameter(Database.GetParameterName("id"), DbType.Guid, (object)id);
using var r = cmd.ExecuteReader();
if (r.Read())
return ReadTask(r);
return null;
protected virtual MoveTask ReadTask(IDataReader r)
{
var source = r.GetStringIfNotNull("Source");
var destination = r.GetStringIfNotNull("Destination");
return new MoveTask
{
Id = r.GetGuid(r.GetOrdinal("Id")),
Created = r.GetDateTime(r.GetOrdinal("Created")),
StartDate = r.GetDateTimeIfNotNull("StartDate"),
EndDate = r.GetDateTimeIfNotNull("EndDate"),
ExternalId = r.GetStringIfNotNull("ExternalId"),
TaskType = (TransportTaskType)(r.GetInt32IfNotNull("Type") ?? 0),
Source = string.IsNullOrEmpty(source) ? null : Deserialize<Address>(source),
Destination = string.IsNullOrEmpty(destination) ? null : Deserialize<Address>(destination),
Priority = r.GetInt32IfNotNull("Priority") ?? 0,
};
}
Extension methods used:
SetCommand(...)AddParameter(...)GetStringIfNotNullGetDateTimeIfNotNullGetInt32IfNotNull