Add full Markdown documentation set

This commit is contained in:
root
2026-02-26 07:49:08 +01:00
parent faf45eed1e
commit 984870f510
10 changed files with 618 additions and 0 deletions

37
docs/README.md Normal file
View File

@@ -0,0 +1,37 @@
# DynamORM Documentation
This documentation is based on:
- XML comments in the library source (`DynamORM/*.cs`, `DynamORM/*/*.cs`).
- Executable usage patterns from the test suite (`DynamORM.Tests`).
## Contents
- [Quick Start](quick-start.md)
- [Dynamic Table API](dynamic-table-api.md)
- [Fluent Builder API](fluent-builder-api.md)
- [Mapping and Entities](mapping-and-entities.md)
- [Transactions and Disposal](transactions-and-disposal.md)
- [Stored Procedures](stored-procedures.md)
- [.NET 4.0 Amalgamation](net40-amalgamation.md)
- [Test-Driven Examples](test-driven-examples.md)
## Supported Targets
Main library targets are defined in `DynamORM/DynamORM.csproj`.
For legacy .NET 4.0 consumers, use the amalgamated source workflow documented in [.NET 4.0 Amalgamation](net40-amalgamation.md).
## Design Overview
DynamORM provides two major usage styles:
- Dynamic table access:
- `db.Table("users").Count(...)`
- `db.Table("users").Insert(...)`
- `db.Table("users").Query(...)`
- Fluent SQL builder access:
- `db.From<T>()`
- `db.Select<T>()`
- `db.Insert<T>() / db.Update<T>() / db.Delete<T>()`
The dynamic API is concise and fast to use. The fluent builder API gives stronger composition and explicit SQL control.

80
docs/dynamic-table-api.md Normal file
View File

@@ -0,0 +1,80 @@
# Dynamic Table API
The dynamic table API centers on `DynamicTable` and allows expressive calls like:
```csharp
dynamic users = db.Table("users");
```
## Read Operations
Examples backed by `DynamORM.Tests/Select/DynamicAccessTests.cs`:
```csharp
users.Count(columns: "id");
users.First(columns: "id");
users.Last(columns: "id");
users.Min(columns: "id");
users.Max(columns: "id");
users.Avg(columns: "id");
users.Sum(columns: "id");
users.Scalar(columns: "first", id: 19);
users.Single(id: 19);
users.Query(columns: "first,last", order: "id:desc");
```
## Conditions with `DynamicColumn`
```csharp
users.Count(where: new DynamicColumn("id").Greater(100));
users.Count(where: new DynamicColumn("login").Like("Hoyt.%"));
users.Count(where: new DynamicColumn("id").Between(75, 100));
users.Count(where: new DynamicColumn("id").In(75, 99, 100));
```
## Insert
```csharp
users.Insert(code: "201", first: "Juri", last: "Gagarin");
users.Insert(values: new
{
code = "202",
first = "Juri",
last = "Gagarin"
});
```
## Update
```csharp
users.Update(id: 1, first: "Yuri", last: "Gagarin");
users.Update(
values: new { first = "Yuri" },
where: new { id = 1 });
```
## Delete
```csharp
users.Delete(code: "201");
users.Delete(where: new { id = 14, code = 14 });
```
## Typed Dynamic Table Calls
`DynamicTable` methods also accept `type: typeof(T)` for mapped class scenarios:
```csharp
users.Count(type: typeof(User), columns: "id");
users.Query(type: typeof(User));
```
These usage patterns are covered in `DynamORM.Tests/Select/TypedAccessTests.cs`.
## Notes
- Dynamic member names map to table/column names and builder conventions.
- Unknown dynamic operations throw `InvalidOperationException`.
- Explicit schema behavior depends on `DynamicDatabaseOptions.SupportSchema`.

View File

@@ -0,0 +1,84 @@
# Fluent Builder API
The fluent API is built around interfaces like `IDynamicSelectQueryBuilder`, `IDynamicInsertQueryBuilder`, `IDynamicUpdateQueryBuilder`, and `IDynamicDeleteQueryBuilder`.
## Core Select Flow
```csharp
using (var query = db.From("users", "u")
.Where("u.id", 19)
.SelectColumn("u.first", "u.last"))
{
var result = query.Execute().ToList();
}
```
## Typed Select
```csharp
using (var query = db.From<User>("u")
.Where(x => x.u.id == 19)
.Select(x => x.u.All()))
{
var users = query.Execute<User>().ToList();
}
```
## Parser Lambda Patterns
The parser supports patterns tested in `DynamORM.Tests/Select/ParserTests.cs`:
- `From(x => x.dbo.Users)`
- `From(x => x.dbo.Users.As(x.u))`
- `Join(x => x.Left().Accounts.As(x.a).On(x.a.userId == x.u.id))`
- `Where(x => x.Or(x.u.id > 100))`
- `Select(x => x.u.first.As(x.firstName))`
- `GroupBy(x => x.u.last)`
- `Having(x => x.Count() > 1)`
- `OrderBy(x => x.u.id.Desc())`
## Subqueries
```csharp
var sub = new DynamicSelectQueryBuilder(db).From(x => x.dbo.Users);
using (var query = new DynamicSelectQueryBuilder(db)
.From(x => x(sub).As("u"))
.Select(x => x.u.All()))
{
var rows = query.Execute().ToList();
}
```
## Scalar Helpers
```csharp
var count = db.From<User>()
.Select(x => x.Count())
.ScalarAs<int>();
```
## Modify Builders
```csharp
db.Insert("users").Values("code", "301").Values("first", "Ada").Execute();
db.Update("users")
.Values("first", "Alicia")
.Where("id", 301)
.Execute();
db.Delete("users")
.Where("id", 301)
.Execute();
```
## SQL Inspection
You can inspect generated SQL from builder objects:
```csharp
var sql = query.CommandText();
```
Command text assertions are heavily used in parser tests.

View File

@@ -0,0 +1,101 @@
# Mapping and Entities
DynamORM supports attribute-driven mapping and entity lifecycle helpers.
## Mapping Attributes
## `TableAttribute`
```csharp
[Table(Name = "users", Owner = "dbo", Override = true)]
public class User
{
// ...
}
```
- `Name`: table name override.
- `Owner`: schema/owner segment.
- `Override`: prefer attribute schema info over provider schema.
## `ColumnAttribute`
```csharp
public class User
{
[Column("id", isKey: true)]
public int Id { get; set; }
[Column("email")]
public string Email { get; set; }
}
```
Important flags:
- `IsKey`
- `AllowNull`
- `IsNoInsert`
- `IsNoUpdate`
- `Type`, `Size`, `Precision`, `Scale`
## Ignore Fields
Use `IgnoreAttribute` to skip properties in mapper-driven workflows.
## Validation
`RequiredAttribute` can define value rules.
```csharp
public class Profile
{
[Required(1f, 10f)]
public int Rank { get; set; }
[Required(2, true)]
[Required(7, 18, ElementRequirement = true)]
public decimal[] Scores { get; set; }
}
var issues = DynamicMapperCache.GetMapper<Profile>().ValidateObject(profile);
```
Validation scenarios are verified in `DynamORM.Tests/Helpers/Validation/ObjectValidationTest.cs`.
## `DynamicEntityBase`
`DynamicEntityBase` tracks state and changed fields.
```csharp
public class UserEntity : DynamicEntityBase
{
[Column("id", true)]
public int Id { get; set; }
[Column("first")]
public string First { get; set; }
}
```
Available behaviors:
- `Validate()`
- `Insert(db)`
- `Update(db)`
- `Delete(db)`
- `Refresh(db)`
- `Save(db)` driven by `DynamicEntityState`
## Repository Base
`DynamicRepositoryBase<T>` provides common operations:
- `GetAll()`
- `GetByQuery(...)`
- `Insert(...)`
- `Update(...)`
- `Delete(...)`
- `Save(...)`
It ensures query/table compatibility for `T` unless explicitly bypassed.

View File

@@ -0,0 +1,42 @@
# .NET 4.0 Amalgamation
Legacy .NET 4.0 consumers should use the amalgamated source model.
## Files
- `AmalgamationTool/DynamORM.Amalgamation.cs`: merged source file.
- `DynamORM.Net40.csproj`: net40 build project that compiles the amalgamation output.
## Regenerate Amalgamation
The amalgamation file must be regenerated after library source changes.
Current workflow in this repository:
1. Build or run `AmalgamationTool` (or use an equivalent merge script).
2. Merge all files from `DynamORM/` into `AmalgamationTool/DynamORM.Amalgamation.cs`.
3. Build `DynamORM.Net40.csproj`.
## Build in Mono Container
```bash
docker run --rm -v "$PWD":/src -w /src mono:latest \
bash -lc "msbuild /t:Build /p:Configuration=Release DynamORM.Net40.csproj"
```
Expected output artifact:
- `bin/Release/net40/DynamORM.Net40.dll`
## Build with .NET SDK Container
```bash
docker run --rm -v "$PWD":/src -w /src mcr.microsoft.com/dotnet/sdk:10.0-preview \
dotnet build DynamORM.Net40.csproj -c Release
```
## Important Constraint
Do not force net40 into the main multi-target `DynamORM/DynamORM.csproj` for modern workflows.
Keep net40 compatibility validated through the dedicated amalgamation project so legacy consumers can embed the single-file source without destabilizing primary targets.

65
docs/quick-start.md Normal file
View File

@@ -0,0 +1,65 @@
# Quick Start
## Install and Reference
Reference the `DynamORM` project or package from your application.
## Basic Setup
```csharp
using System.Data.SQLite;
using DynamORM;
var options =
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportSchema;
using (var db = new DynamicDatabase(
SQLiteFactory.Instance,
"Data Source=app.db;",
options))
{
db.DumpCommands = true;
var users = db.Table("users");
var total = users.Count(columns: "id");
var first = users.First(columns: "id,first,last");
}
```
This setup mirrors `DynamORM.Tests/TestsBase.cs`.
## First Query (Dynamic API)
```csharp
using (var db = new DynamicDatabase(SQLiteFactory.Instance, "Data Source=app.db;", options))
{
var row = db.Table("users").Single(id: 19);
Console.WriteLine(row.first);
}
```
## First Query (Fluent Builder API)
```csharp
using (var db = new DynamicDatabase(SQLiteFactory.Instance, "Data Source=app.db;", options))
using (var query = db.From("users").Where("id", 19).SelectColumn("first"))
{
var first = query.ScalarAs<string>();
Console.WriteLine(first);
}
```
## Insert, Update, Delete
```csharp
var table = db.Table("users");
table.Insert(code: "201", first: "Juri", last: "Gagarin");
table.Update(values: new { first = "Yuri" }, where: new { code = "201" });
table.Delete(code: "201");
```
These forms are validated in `DynamORM.Tests/Modify/DynamicModificationTests.cs`.

51
docs/stored-procedures.md Normal file
View File

@@ -0,0 +1,51 @@
# Stored Procedures
Stored procedure support is available through `db.Procedures` when `DynamicDatabaseOptions.SupportStoredProcedures` is enabled.
## Basic Invocation
```csharp
var scalar = db.Procedures.sp_Exp_Scalar();
var scalarTyped = db.Procedures.sp_Exp_Scalar<int>();
```
## Input, Output, Return Parameters
Prefixes in argument names control parameter direction:
- `out_` for output
- `ret_` for return value
- `both_` for input/output
Example pattern:
```csharp
var res = db.Procedures.sp_Exp_SomeInputAndOutput<
string,
MyProcResult>(
Name: "G4g4r1n",
out_Result: new DynamicSchemaColumn { Size = 256 },
ret_Return: 0);
```
## Nested Procedure Names
Dynamic member chaining builds qualified names:
```csharp
var res = db.Procedures.dbo.reporting.MyProc();
```
This resolves to `dbo.reporting.MyProc`.
## Result Mapping
If generic return types are provided, DynamORM attempts mapper-based projection into the target type.
If output parameters are present, result payload is assembled from:
- main scalar/resultset-derived value
- output values
- optional return value
The behavior is implemented in `DynamORM/DynamicProcedureInvoker.cs` and documented in XML examples in `DynamORM/DynamicDatabase.cs`.

View File

@@ -0,0 +1,70 @@
# Test-Driven Examples
This page maps concrete examples to test files so behavior can be verified quickly.
## Dynamic Select and Aggregates
Source: `DynamORM.Tests/Select/DynamicAccessTests.cs`
- `Count/Min/Max/Avg/Sum/Scalar`
- `First/Last/Single`
- `IN`, `LIKE`, `BETWEEN`, comparison operators
- ad-hoc column expressions and aliases
## Typed Access and Mapping
Source: `DynamORM.Tests/Select/TypedAccessTests.cs`
- `Query(type: typeof(T))`
- `Execute<T>()`
- projection with `MapEnumerable<T>()`
- table mapping and strongly typed expressions
## SQL Parser and CommandText Expectations
Source: `DynamORM.Tests/Select/ParserTests.cs`
- `From` variants (`schema.table`, aliasing, string forms)
- join expressions and join kind helpers
- subquery embedding
- `NoLock()` translation
- deterministic SQL text generation checks
## Data Modification
Source: `DynamORM.Tests/Modify/DynamicModificationTests.cs`
- insert via named args, anonymous object, mapped class, plain class
- update via key/object/where
- delete via key/object/where
## Schema Variants
Sources:
- `DynamORM.Tests/Modify/DynamicTypeSchemaModificationTests.cs`
- `DynamORM.Tests/Modify/DynamicNoSchemaModificationTests.cs`
- `DynamORM.Tests/Select/DynamicTypeSchemaAccessTests.cs`
- `DynamORM.Tests/Select/DynamicNoSchemaAccessTests.cs`
Use these to validate behavior with and without schema support.
## Resource and Pooling Semantics
Source: `DynamORM.Tests/Helpers/PoolingTests.cs`
- command invalidation after database disposal
- transaction rollback behavior during disposal
## Validation
Source: `DynamORM.Tests/Helpers/Validation/ObjectValidationTest.cs`
- rule-based object validation with `RequiredAttribute`
- array element-level validation
## Dynamic Parser (Helper Layer)
Source: `DynamORM.Tests/Helpers/Dynamic/DynamicParserTests.cs`
Covers lower-level parser behavior used by fluent lambda parsing.

View File

@@ -0,0 +1,65 @@
# Transactions and Disposal
DynamORM manages connections, command pools, and transaction stacks internally.
## Connection and Transaction Options
`DynamicDatabaseOptions` controls behavior:
- `SingleConnection`
- `SingleTransaction`
- `SupportSchema`
- `SupportStoredProcedures`
- `SupportNoLock`
- `SupportTop` / `SupportLimitOffset` / `SupportFirstSkip`
Typical setup:
```csharp
var options =
DynamicDatabaseOptions.SingleConnection |
DynamicDatabaseOptions.SingleTransaction |
DynamicDatabaseOptions.SupportLimitOffset |
DynamicDatabaseOptions.SupportSchema;
```
## Transaction Usage
```csharp
using (var db = new DynamicDatabase(factory, connectionString, options))
using (var conn = db.Open())
using (var tx = conn.BeginTransaction())
using (var cmd = conn.CreateCommand())
{
cmd.SetCommand("UPDATE users SET first = 'Ada' WHERE id = 1").ExecuteNonQuery();
tx.Commit();
}
```
Global transaction mode is also available via `db.BeginTransaction()`.
## Disposal Guarantees
Current disposal behavior includes idempotent guards on key objects:
- `DynamicDatabase`
- `DynamicConnection`
- `DynamicCommand`
- `DynamicTransaction`
- `DynamicTable`
- builder and parser helpers
This prevents repeated cleanup from throwing or re-disposing lower-level resources.
## Pooling and Rollback Behavior
Behavior validated by `DynamORM.Tests/Helpers/PoolingTests.cs`:
- Disposing the database invalidates active commands.
- Open transactions are rolled back during disposal when not committed.
## Practices
- Prefer `using` blocks for `DynamicDatabase`, connections, commands, transactions, and builders.
- Do not manually re-dispose the same object from multiple ownership paths unless `IsDisposed` is checked.
- Keep transaction scope short and explicit.