Add full Markdown documentation set
This commit is contained in:
23
README.md
Normal file
23
README.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# DynamORM
|
||||||
|
|
||||||
|
Dynamic ORM and SQL builder for .NET, with dynamic and typed APIs.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Full documentation is available in [`docs/README.md`](docs/README.md):
|
||||||
|
|
||||||
|
- Quick start
|
||||||
|
- Dynamic table API
|
||||||
|
- Fluent builder API
|
||||||
|
- Mapping and entity lifecycle
|
||||||
|
- Transaction/disposal semantics
|
||||||
|
- Stored procedures
|
||||||
|
- .NET 4.0 amalgamation workflow
|
||||||
|
- Test-driven examples
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
|
||||||
|
- `DynamORM/`: core library
|
||||||
|
- `DynamORM.Tests/`: test suite
|
||||||
|
- `AmalgamationTool/`: amalgamation generator and generated single-file output
|
||||||
|
- `DynamORM.Net40.csproj`: net40 build for amalgamated source compatibility
|
||||||
37
docs/README.md
Normal file
37
docs/README.md
Normal 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
80
docs/dynamic-table-api.md
Normal 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`.
|
||||||
84
docs/fluent-builder-api.md
Normal file
84
docs/fluent-builder-api.md
Normal 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.
|
||||||
101
docs/mapping-and-entities.md
Normal file
101
docs/mapping-and-entities.md
Normal 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.
|
||||||
42
docs/net40-amalgamation.md
Normal file
42
docs/net40-amalgamation.md
Normal 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
65
docs/quick-start.md
Normal 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
51
docs/stored-procedures.md
Normal 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`.
|
||||||
70
docs/test-driven-examples.md
Normal file
70
docs/test-driven-examples.md
Normal 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.
|
||||||
65
docs/transactions-and-disposal.md
Normal file
65
docs/transactions-and-disposal.md
Normal 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.
|
||||||
Reference in New Issue
Block a user