Expand docs with syntax/procedure deep dive and ADO.NET extensions
This commit is contained in:
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