Expand docs with syntax/procedure deep dive and ADO.NET extensions
This commit is contained in:
@@ -9,9 +9,11 @@ Full documentation is available in [`docs/README.md`](docs/README.md):
|
|||||||
- Quick start
|
- Quick start
|
||||||
- Dynamic table API
|
- Dynamic table API
|
||||||
- Fluent builder API
|
- Fluent builder API
|
||||||
|
- Syntax and procedure deep dive
|
||||||
- Mapping and entity lifecycle
|
- Mapping and entity lifecycle
|
||||||
- Transaction/disposal semantics
|
- Transaction/disposal semantics
|
||||||
- Stored procedures
|
- Stored procedures
|
||||||
|
- ADO.NET extensions and cached reader
|
||||||
- .NET 4.0 amalgamation workflow
|
- .NET 4.0 amalgamation workflow
|
||||||
- Test-driven examples
|
- Test-driven examples
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ This documentation is based on:
|
|||||||
- [Quick Start](quick-start.md)
|
- [Quick Start](quick-start.md)
|
||||||
- [Dynamic Table API](dynamic-table-api.md)
|
- [Dynamic Table API](dynamic-table-api.md)
|
||||||
- [Fluent Builder API](fluent-builder-api.md)
|
- [Fluent Builder API](fluent-builder-api.md)
|
||||||
|
- [Syntax and Procedure Deep Dive](syntax-and-procedures-deep-dive.md)
|
||||||
- [Mapping and Entities](mapping-and-entities.md)
|
- [Mapping and Entities](mapping-and-entities.md)
|
||||||
- [Transactions and Disposal](transactions-and-disposal.md)
|
- [Transactions and Disposal](transactions-and-disposal.md)
|
||||||
- [Stored Procedures](stored-procedures.md)
|
- [Stored Procedures](stored-procedures.md)
|
||||||
|
- [ADO.NET Extensions](ado-net-extensions.md)
|
||||||
- [.NET 4.0 Amalgamation](net40-amalgamation.md)
|
- [.NET 4.0 Amalgamation](net40-amalgamation.md)
|
||||||
- [Test-Driven Examples](test-driven-examples.md)
|
- [Test-Driven Examples](test-driven-examples.md)
|
||||||
|
|
||||||
|
|||||||
163
docs/ado-net-extensions.md
Normal file
163
docs/ado-net-extensions.md
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
# 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.
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Stored procedure support is available through `db.Procedures` when `DynamicDatabaseOptions.SupportStoredProcedures` is enabled.
|
Stored procedure support is available through `db.Procedures` when `DynamicDatabaseOptions.SupportStoredProcedures` is enabled.
|
||||||
|
|
||||||
|
For an in-depth comparison with query syntax styles and advanced return-shape behavior, see [Syntax Deep Dive and Procedure Calls](syntax-and-procedures-deep-dive.md).
|
||||||
|
|
||||||
## Basic Invocation
|
## Basic Invocation
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
|
|||||||
173
docs/syntax-and-procedures-deep-dive.md
Normal file
173
docs/syntax-and-procedures-deep-dive.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# Syntax Deep Dive and Procedure Calls
|
||||||
|
|
||||||
|
This guide focuses on the two query syntaxes and advanced stored procedure invocation patterns.
|
||||||
|
|
||||||
|
## Two Query Syntax Styles
|
||||||
|
|
||||||
|
DynamORM supports:
|
||||||
|
|
||||||
|
- Dynamic table syntax: concise runtime calls via `dynamic`.
|
||||||
|
- Fluent builder syntax: explicit SQL composition via interfaces and lambda parser.
|
||||||
|
|
||||||
|
## Dynamic Table Syntax
|
||||||
|
|
||||||
|
Entry point:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
dynamic users = db.Table("users");
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical reads:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var count = users.Count(columns: "id");
|
||||||
|
var first = users.First(columns: "id,first,last");
|
||||||
|
var one = users.Single(id: 19);
|
||||||
|
var list = (users.Query(columns: "id,first", order: "id:desc") as IEnumerable<dynamic>).ToList();
|
||||||
|
```
|
||||||
|
|
||||||
|
Dynamic filters with `DynamicColumn`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
users.Count(where: new DynamicColumn("id").Greater(100));
|
||||||
|
users.Count(where: new DynamicColumn("last").In("Hendricks", "Goodwin", "Freeman"));
|
||||||
|
```
|
||||||
|
|
||||||
|
Modifications:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
users.Insert(code: "201", first: "Juri", last: "Gagarin");
|
||||||
|
users.Update(values: new { first = "Yuri" }, where: new { code = "201" });
|
||||||
|
users.Delete(code: "201");
|
||||||
|
```
|
||||||
|
|
||||||
|
Coverage references:
|
||||||
|
|
||||||
|
- `DynamORM.Tests/Select/DynamicAccessTests.cs`
|
||||||
|
- `DynamORM.Tests/Modify/DynamicModificationTests.cs`
|
||||||
|
|
||||||
|
## Fluent Builder Syntax
|
||||||
|
|
||||||
|
Entry points:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var q1 = db.From("users", "u");
|
||||||
|
var q2 = db.From<Users>("u");
|
||||||
|
```
|
||||||
|
|
||||||
|
Composable select:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using (var query = db.From<Users>("u")
|
||||||
|
.Where(x => x.u.id > 10)
|
||||||
|
.Select(x => x.u.id, x => x.u.first)
|
||||||
|
.OrderBy(x => x.u.id.Desc()))
|
||||||
|
{
|
||||||
|
var rows = query.Execute().ToList();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Parser forms supported by tests:
|
||||||
|
|
||||||
|
- `From(x => x.dbo.Users)`
|
||||||
|
- `From(x => x.dbo.Users.As(x.u))`
|
||||||
|
- `From(x => "dbo.Users AS u")`
|
||||||
|
- `From(x => x(subQuery).As("u"))`
|
||||||
|
- `Join(x => x.Left().Accounts.As(x.a).On(x.a.userId == x.u.id))`
|
||||||
|
|
||||||
|
Coverage references:
|
||||||
|
|
||||||
|
- `DynamORM.Tests/Select/ParserTests.cs`
|
||||||
|
- `DynamORM.Tests/Select/LegacyParserTests.cs`
|
||||||
|
|
||||||
|
## Choosing Between Syntaxes
|
||||||
|
|
||||||
|
Use dynamic table syntax when:
|
||||||
|
|
||||||
|
- You want short CRUD calls quickly.
|
||||||
|
- Table/column selection is runtime-driven.
|
||||||
|
|
||||||
|
Use fluent builder syntax when:
|
||||||
|
|
||||||
|
- You want explicit SQL structure and deterministic command text.
|
||||||
|
- You need complex joins/subqueries/parser features.
|
||||||
|
- You prefer typed projections (`Execute<T>()`, `ScalarAs<T>()`).
|
||||||
|
|
||||||
|
## Stored Procedure Deep Dive
|
||||||
|
|
||||||
|
`db.Procedures` provides dynamic invocation of stored procedures.
|
||||||
|
|
||||||
|
## Basic Calls
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var r0 = db.Procedures.sp_Exp_Scalar();
|
||||||
|
var r1 = db.Procedures.sp_Exp_Scalar<int>();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Namespaced Procedure Names
|
||||||
|
|
||||||
|
`TryGetMember` composes member segments before invocation:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var r = db.Procedures.dbo.reporting.sp_MonthlySales();
|
||||||
|
```
|
||||||
|
|
||||||
|
Final procedure name becomes `dbo.reporting.sp_MonthlySales`.
|
||||||
|
|
||||||
|
## Direction Prefixes
|
||||||
|
|
||||||
|
Argument name prefixes drive parameter direction:
|
||||||
|
|
||||||
|
- `out_` => `ParameterDirection.Output`
|
||||||
|
- `ret_` => `ParameterDirection.ReturnValue`
|
||||||
|
- `both_` => `ParameterDirection.InputOutput`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
dynamic res = db.Procedures.sp_Test_Scalar_In_Out(
|
||||||
|
inp: Guid.NewGuid(),
|
||||||
|
out_outp: Guid.Empty,
|
||||||
|
ret_Return: 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
Returned object exposes values without prefixes.
|
||||||
|
|
||||||
|
## Advanced Parameter Shape
|
||||||
|
|
||||||
|
You can pass:
|
||||||
|
|
||||||
|
- plain values
|
||||||
|
- `DynamicColumn` with schema and direction metadata
|
||||||
|
- `DynamicSchemaColumn`
|
||||||
|
- `DynamicExpando` / `ExpandoObject`
|
||||||
|
|
||||||
|
Example with explicit output schema:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var res = db.Procedures.sp_Exp_SomeInputAndOutput<string, MyProcResult>(
|
||||||
|
Name: "G4g4r1n",
|
||||||
|
out_Result: new DynamicSchemaColumn { Name = "Result", Size = 256 },
|
||||||
|
ret_Return: 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generic Result Shapes
|
||||||
|
|
||||||
|
From `DynamicProcedureInvoker` behavior:
|
||||||
|
|
||||||
|
- `T == IDataReader`: returns cached reader (`CachedReader()`).
|
||||||
|
- `T == DataTable`: materializes via `ToDataTable()`.
|
||||||
|
- `T == IEnumerable<object>`: dynamic row enumeration.
|
||||||
|
- `T == IEnumerable<primitive>`: scalar conversion per row.
|
||||||
|
- `T == IEnumerable<class>`: mapping with mapper cache.
|
||||||
|
- `T == class`: mapped single/object payload.
|
||||||
|
|
||||||
|
## Return and Output Aggregation
|
||||||
|
|
||||||
|
When output/return params are present, DynamORM aggregates:
|
||||||
|
|
||||||
|
- main result
|
||||||
|
- each output parameter
|
||||||
|
- return value
|
||||||
|
|
||||||
|
into a dynamic object or mapped result type (if a mapping type is provided).
|
||||||
Reference in New Issue
Block a user