# ADO.NET Extensions Reference DynamORM exposes extension helpers mainly in: - `DynamORM/DynamicExtensions.cs` - `DynamORM/Helpers/DataReaderExtensions.cs` - `DynamORM/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: ```csharp 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 `object` when calling `AddParameter(...)` 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: ```csharp 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()` - `ExecuteScalarAs(defaultValue)` - `ExecuteScalarAs(TryParseHandler)` - `ExecuteScalarAs(defaultValue, TryParseHandler)` - `ExecuteEnumeratorOf(defaultValue, TryParseHandler)` 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 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: - `GetBooleanIfNotNull` - `GetByteIfNotNull` - `GetCharIfNotNull` - `GetDateTimeIfNotNull` - `GetDecimalIfNotNull` - `GetDoubleIfNotNull` - `GetFloatIfNotNull` - `GetGuidIfNotNull` - `GetInt16IfNotNull` - `GetInt32IfNotNull` - `GetInt64IfNotNull` - `GetStringIfNotNull` - `GetValueIfNotNull` 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(...)` - `DynamicCachedReader.FromEnumerable(Type, IEnumerable)` Typical usage: ```csharp 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 ```csharp var records = new Dictionary(); 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)` - `GetStringIfNotNull` - `GetInt32IfNotNull` - `GetDateTimeIfNotNull` ## Sample 2: Stored Procedure with Input/Output Parameters + Result Check ```csharp 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 with `DbType` and `ParameterDirection` - `DumpToString()` (optional diagnostic logging) ## Sample 3: Parameterized `UPDATE` with Runtime SQL and ADO.NET Extensions ```csharp 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 ```csharp 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
(source), Destination = string.IsNullOrEmpty(destination) ? null : Deserialize
(destination), Priority = r.GetInt32IfNotNull("Priority") ?? 0, }; } ``` Extension methods used: - `SetCommand(...)` - `AddParameter(...)` - `GetStringIfNotNull` - `GetDateTimeIfNotNull` - `GetInt32IfNotNull`