Skip to content

Add typed DTO selection, updates and deletes - #206

Open
jogibear9988 wants to merge 5 commits into
masterfrom
codex/dto-select-predicates
Open

jogibear9988 wants to merge 5 commits into
masterfrom
codex/dto-select-predicates

Conversation

@jogibear9988

@jogibear9988 jogibear9988 commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Add migration-local typed DTO selection, updates and deletes with parameterized SQL and normal predicate lambdas. String predicates support Contains(string), StartsWith(string) and EndsWith(string).

Public API

Extension methods in DotNetProjects.Migrator.Framework:

public static IReadOnlyList<T> Select<T>(
    this ITransformationProvider provider,
    string table,
    Expression<Func<T, bool>> predicate = null)
    where T : class, new();

public static int Update<T>(
    this ITransformationProvider provider,
    string table,
    Expression<Func<T, T>> changes,
    Expression<Func<T, bool>> predicate)
    where T : class, new();

public static int Delete<T>(
    this ITransformationProvider provider,
    string table,
    Expression<Func<T, bool>> predicate)
    where T : class, new();

The table name is explicit. Selection returns materialized DTOs; mutations return the driver-reported affected row count.

DTO mapping

using DotNetProjects.Migrator.Framework;
using DtoColumn = System.ComponentModel.DataAnnotations.Schema.ColumnAttribute;

public sealed class ScreenDto
{
    public int Id { get; set; }
    public bool Archive { get; set; }
    [DtoColumn("GenericDefinition")]
    public string Definition { get; set; }
}

Properties map to columns by name; standard ColumnAttribute overrides names. The alias avoids Migrator's existing ColumnAttribute enum. Inherited public properties are supported; standard NotMappedAttribute excludes properties. DTOs need a public parameterless constructor and public readable/writable scalar properties.

Usage: select, string filtering, update and delete

Assuming provider is the migration's ITransformationProvider:

// Comparisons, grouping and literal substring searching.
var screens = provider.Select<ScreenDto>(
    "Common_Screens",
    x => (!x.Archive && x.Id > 4) || x.Definition.Contains("aa"));

// Other supported string predicates.
var prefixed = provider.Select<ScreenDto>(
    "Common_Screens", x => x.Definition.StartsWith("old"));
var suffixed = provider.Select<ScreenDto>(
    "Common_Screens", x => x.Definition.EndsWith(".obsolete"));

// Change exactly these two columns; Id and other properties are untouched.
int updated = provider.Update<ScreenDto>(
    "Common_Screens",
    x => new ScreenDto { Archive = true, Definition = "archived" },
    x => x.Id > 4 && x.Definition.StartsWith("old"));

// Delete only rows matching the required predicate.
int deleted = provider.Delete<ScreenDto>(
    "Common_Screens",
    x => x.Archive && x.Definition.EndsWith(".obsolete"));

// Selection can omit the predicate to read all rows.
var allScreens = provider.Select<ScreenDto>("Common_Screens");

Inside a Classic migration use Database.Select<T>, Database.Update<T> and Database.Delete<T>. In a FluentMigration, use the corresponding Context methods for immediate operations; these cannot see schema changes merely queued in the same builder.

Behavior and limits

  • Direct comparisons, &&, ||, !, grouping, null checks, constants and captured scalar fields are translated into SQL; there is no implicit client-side filtering.
  • String functions support the single-string overload on mapped properties. Search text is parameterized; %, _, the escape character, and SQL Server/Sybase bracket wildcards are treated literally. Character and StringComparison overloads are unsupported.
  • Predicates follow SQL null and database comparison/LIKE semantics, rather than .NET ordinal matching. NULL string columns remain excluded under negation; empty search text matches non-NULL values, and null search text throws.
  • Updates require a non-empty DTO initializer. Only assigned properties are changed; explicit null assignments become SQL NULL. Values must be constants or captured scalar fields; row-dependent arithmetic, unmapped assignments and constructor arguments are rejected.
  • Both mutations require a non-null predicate; there are no predicate-free typed update/delete overloads.
  • Table/column names use provider quoting, values use native driver parameter conversions, and positional parameters preserve SQL occurrence order.
  • Commands/readers are disposed on success and failure. Operations retain command timeouts and the active migration transaction. DTO mapping errors name columns/properties; selection row order is unspecified.
  • Other methods, joins, navigation/property-to-property comparisons and general LINQ operators are unsupported. Ordering on strings, booleans, GUIDs and enums, and byte-array comparisons, are unsupported.

Part 1 of the implementation for #202. The SQL interpolation proposal is implemented separately in #208. Merge this PR first, then retarget #208 to master.

Validation:

  • Solution build passed.
  • 882 unit tests and 484 SQLite tests passed.
  • Regression coverage includes string escaping, grouping/null semantics, mutation affected counts, selective assignments, transactions and resource disposal.
  • Oracle/HANA/Informix binding tests cover select/update/delete; the eleven-database CI fixture covers escaped string predicates and typed mutations using native drivers.
  • Documentation tests/link checks passed; all 110 published C# samples compiled and 8 SQLite parity checks passed.
  • Non-SQLite live databases were not run locally.

Generated documentation includes renumbered example IDs after adding the original DTO sample.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 19:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 19:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 19:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@jogibear9988 jogibear9988 changed the title Add DTO selection with type-safe predicate expressions Add typed DTO selection, updates and deletes Oct 14, 2026
Copilot AI balanced review requested due to automatic review settings October 14, 2026 19:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 19:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants