Files
DynamORM/docs/syntax-and-procedures-deep-dive.md

4.0 KiB

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:

dynamic users = db.Table("users");

Typical reads:

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:

users.Count(where: new DynamicColumn("id").Greater(100));
users.Count(where: new DynamicColumn("last").In("Hendricks", "Goodwin", "Freeman"));

Modifications:

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:

var q1 = db.From("users", "u");
var q2 = db.From<Users>("u");

Composable select:

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

var r0 = db.Procedures.sp_Exp_Scalar();
var r1 = db.Procedures.sp_Exp_Scalar<int>();

Namespaced Procedure Names

TryGetMember composes member segments before invocation:

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:

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:

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).