323 lines
9.1 KiB
Markdown
323 lines
9.1 KiB
Markdown
# 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`.
|
|
|
|
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<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:
|
|
|
|
- `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<T>(...)`
|
|
- `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<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)`
|
|
- `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<Address>(source),
|
|
Destination = string.IsNullOrEmpty(destination) ? null : Deserialize<Address>(destination),
|
|
Priority = r.GetInt32IfNotNull("Priority") ?? 0,
|
|
};
|
|
}
|
|
```
|
|
|
|
Extension methods used:
|
|
|
|
- `SetCommand(...)`
|
|
- `AddParameter(...)`
|
|
- `GetStringIfNotNull`
|
|
- `GetDateTimeIfNotNull`
|
|
- `GetInt32IfNotNull`
|