Table of Contents

Class Repository<T>

Namespace
CL.MySQL2.Services
Assembly
CL.MySQL2.dll

Generic repository providing CRUD for entity type T. Uses compiled materializers via CL.MySQL2.Core.EntityMetadata<T> — no per-row reflection.

public sealed class Repository<T> where T : class, new()

Type Parameters

T
Inheritance
Repository<T>
Inherited Members

Constructors

Repository(ConnectionManager, ILogger?, TransactionScope, int, int)

public Repository(ConnectionManager connectionManager, ILogger? logger, TransactionScope transactionScope, int slowQueryThresholdMs = 1000, int maxBatchInsertSize = 500)

Parameters

connectionManager ConnectionManager
logger ILogger
transactionScope TransactionScope
slowQueryThresholdMs int
maxBatchInsertSize int

Repository(ConnectionManager, ILogger?, TransactionScope, int, int, bool)

public Repository(ConnectionManager connectionManager, ILogger? logger, TransactionScope transactionScope, int slowQueryThresholdMs, int maxBatchInsertSize, bool distinguishDuplicateKeyError)

Parameters

connectionManager ConnectionManager
logger ILogger
transactionScope TransactionScope
slowQueryThresholdMs int
maxBatchInsertSize int
distinguishDuplicateKeyError bool

Repository(ConnectionManager, ILogger?, string, int, int)

public Repository(ConnectionManager connectionManager, ILogger? logger = null, string connectionId = "Default", int slowQueryThresholdMs = 1000, int maxBatchInsertSize = 500)

Parameters

connectionManager ConnectionManager
logger ILogger
connectionId string
slowQueryThresholdMs int
maxBatchInsertSize int

Repository(ConnectionManager, ILogger?, string, int, int, bool)

public Repository(ConnectionManager connectionManager, ILogger? logger, string connectionId, int slowQueryThresholdMs, int maxBatchInsertSize, bool distinguishDuplicateKeyError)

Parameters

connectionManager ConnectionManager
logger ILogger
connectionId string
slowQueryThresholdMs int
maxBatchInsertSize int
distinguishDuplicateKeyError bool

Properties

DistinguishDuplicateKeyError

When true, duplicate key errors (MySQL error 1062) in InsertAsync(T, CancellationToken) and InsertManyAsync(IEnumerable<T>, CancellationToken) return the distinct error code mysql.duplicate_key and log at Warning instead of Error. Default: false.

public bool DistinguishDuplicateKeyError { get; set; }

Property Value

bool

Methods

AdjustAsync<TProperty>(object, Expression<Func<T, TProperty>>, TProperty, CancellationToken)

Atomically adjusts a numeric column by delta. Negative for decrement.

public Task<Result<int>> AdjustAsync<TProperty>(object id, Expression<Func<T, TProperty>> propertySelector, TProperty delta, CancellationToken ct = default)

Parameters

id object
propertySelector Expression<Func<T, TProperty>>
delta TProperty
ct CancellationToken

Returns

Task<Result<int>>

Type Parameters

TProperty

CountAsync(CancellationToken)

Returns the total row count for the table. Like every other repository read this applies the SoftDeleteAttribute filter, so soft-deleted rows are excluded and the count agrees with GetAllAsync(CancellationToken) and GetPagedAsync(int, int, string?, bool, CancellationToken). Use Query<T>().IncludeDeleted().CountAsync() to count deleted rows as well.

public Task<Result<long>> CountAsync(CancellationToken ct = default)

Parameters

ct CancellationToken

Returns

Task<Result<long>>

DecrementAsync<TProperty>(object, Expression<Func<T, TProperty>>, TProperty, CancellationToken)

Decrement a numeric column by amount.

public Task<Result<int>> DecrementAsync<TProperty>(object id, Expression<Func<T, TProperty>> propertySelector, TProperty amount, CancellationToken ct = default)

Parameters

id object
propertySelector Expression<Func<T, TProperty>>
amount TProperty
ct CancellationToken

Returns

Task<Result<int>>

Type Parameters

TProperty

DeleteAsync(object, CancellationToken)

Deletes an entity by its primary key. For a SoftDeleteAttribute entity this is a soft delete — it sets the timestamp column to UtcNow instead of removing the row (no-op if already deleted). Use HardDeleteAsync(object, CancellationToken) to remove the row physically. Returns true when a row was affected.

public Task<Result<bool>> DeleteAsync(object id, CancellationToken ct = default)

Parameters

id object
ct CancellationToken

Returns

Task<Result<bool>>

FindAsync(Expression<Func<T, bool>>, CancellationToken)

Returns all entities matching the given LINQ predicate.

public Task<Result<List<T>>> FindAsync(Expression<Func<T, bool>> predicate, CancellationToken ct = default)

Parameters

predicate Expression<Func<T, bool>>
ct CancellationToken

Returns

Task<Result<List<T>>>

GetAllAsync(CancellationToken)

Retrieves all entities in the table.

public Task<Result<List<T>>> GetAllAsync(CancellationToken ct = default)

Parameters

ct CancellationToken

Returns

Task<Result<List<T>>>

GetByColumnAsync(string, object, CancellationToken)

Retrieves all entities where the specified column equals the given value.

public Task<Result<List<T>>> GetByColumnAsync(string column, object value, CancellationToken ct = default)

Parameters

column string
value object
ct CancellationToken

Returns

Task<Result<List<T>>>

GetByIdAsync(object, CancellationToken)

Retrieves an entity by its primary key value.

public Task<Result<T?>> GetByIdAsync(object id, CancellationToken ct = default)

Parameters

id object
ct CancellationToken

Returns

Task<Result<T>>

GetPagedAsync(int, int, string?, bool, CancellationToken)

Retrieves a paged result set.

public Task<Result<PagedResult<T>>> GetPagedAsync(int page, int pageSize, string? orderByColumn = null, bool descending = false, CancellationToken ct = default)

Parameters

page int
pageSize int
orderByColumn string
descending bool
ct CancellationToken

Returns

Task<Result<PagedResult<T>>>

HardDeleteAsync(object, CancellationToken)

Physically removes the row by primary key, even for soft-delete entities.

public Task<Result<bool>> HardDeleteAsync(object id, CancellationToken ct = default)

Parameters

id object
ct CancellationToken

Returns

Task<Result<bool>>

IncrementAsync<TProperty>(object, Expression<Func<T, TProperty>>, TProperty, CancellationToken)

Increment a numeric column by amount.

public Task<Result<int>> IncrementAsync<TProperty>(object id, Expression<Func<T, TProperty>> propertySelector, TProperty amount, CancellationToken ct = default)

Parameters

id object
propertySelector Expression<Func<T, TProperty>>
amount TProperty
ct CancellationToken

Returns

Task<Result<int>>

Type Parameters

TProperty

InsertAsync(T, CancellationToken)

Inserts a single entity and returns it (with auto-generated PK populated).

public Task<Result<T>> InsertAsync(T entity, CancellationToken ct = default)

Parameters

entity T
ct CancellationToken

Returns

Task<Result<T>>

InsertIgnoreAsync(T, CancellationToken)

Inserts a single entity, ignoring duplicate-key conflicts (UNIQUE index or PRIMARY KEY). If the row was inserted, auto-generated PK (if any) is set on entity and Inserted is returned. If a duplicate exists, Duplicate is returned without raising an error. Non-duplicate errors (data truncation, connection failure, etc.) return a failure Result.

public Task<Result<InsertOutcome>> InsertIgnoreAsync(T entity, CancellationToken ct = default)

Parameters

entity T
ct CancellationToken

Returns

Task<Result<InsertOutcome>>

Remarks

Implementation choice: We use INSERT IGNORE while validating connection warnings. In MySqlConnector, connections use the default "found rows" semantics (UseAffectedRows=false), which causes INSERT ... ON DUPLICATE KEY UPDATE to report matched rows rather than changed rows, returning 1 for duplicates as well as new inserts. In contrast, INSERT IGNORE accurately returns 1 for inserted rows and 0 for ignored duplicates under found-rows semantics. To ensure INSERT IGNORE does not silently swallow severe data errors (such as data truncation or missing NOT NULL values), we intercept server warnings on the connection: any warning other than 1062 (duplicate key entry) is converted into a thrown exception and surfaced as an error.

InsertManyAsync(IEnumerable<T>, CancellationToken)

Bulk-inserts a collection of entities using real batched INSERT statements. Batches of up to the constructor's maxBatchInsertSize are sent per round-trip; MySQL2Library.GetRepository<T> passes the per-database MaxBatchInsertSize configuration value (default 500).

public Task<Result<int>> InsertManyAsync(IEnumerable<T> entities, CancellationToken ct = default)

Parameters

entities IEnumerable<T>
ct CancellationToken

Returns

Task<Result<int>>

InsertManyIgnoreAsync(IEnumerable<T>, CancellationToken)

Bulk-inserts a collection of entities, ignoring duplicate-key conflicts (UNIQUE index or PRIMARY KEY). Batches of up to maxBatchInsertSize are sent per round-trip. Returns the number of rows actually inserted (duplicates contribute 0 to the count).

public Task<Result<int>> InsertManyIgnoreAsync(IEnumerable<T> entities, CancellationToken ct = default)

Parameters

entities IEnumerable<T>
ct CancellationToken

Returns

Task<Result<int>>

UpdateAsync(T, CancellationToken)

Updates an existing entity by PK.

public Task<Result<T>> UpdateAsync(T entity, CancellationToken ct = default)

Parameters

entity T
ct CancellationToken

Returns

Task<Result<T>>

UpsertAsync(T, Expression<Func<T, T, T>>, CancellationToken)

Inserts entity, or — when it collides with an existing row on the primary key or a UNIQUE index — updates that row with onConflict, in one statement. The setter receives the existing row and the row that failed to insert, so the update can build on current values:

await repo.UpsertAsync(
    new LoginThrottle { Ip = ip, Failures = 1, LastUtc = now },
    (current, incoming) => new LoginThrottle
    {
        Failures = current.Failures + 1,
        LastUtc  = incoming.LastUtc,
    });

Columns the setter does not assign keep their current value on conflict. Captured values are sent as parameters. Emits INSERT … ON DUPLICATE KEY UPDATE col = expr, with incoming.Col written as VALUES(col) so it also runs on MariaDB.

MySQL applies the assignments left to right: an assignment that reads current.X after X was assigned sees the new value. Assign a column after the ones that read it if you need its old value.

public Task<Result<int>> UpsertAsync(T entity, Expression<Func<T, T, T>> onConflict, CancellationToken ct = default)

Parameters

entity T
onConflict Expression<Func<T, T, T>>
ct CancellationToken

Returns

Task<Result<int>>

Rows affected (MySQL: 1 = insert, 2 = update with changes, 0 = update with no change).

UpsertAsync(T, CancellationToken)

Inserts a single entity, or updates all non-auto-PK columns to the entity's values if a UNIQUE/PRIMARY-KEY conflict occurs (set semantics). Issues INSERT ... ON DUPLICATE KEY UPDATE col = VALUES(col) — the VALUES(col) form, which both MySQL (all versions) and MariaDB accept; the newer ... AS new row-alias syntax is MySQL 8.0.19+ only and is deliberately not used. On a new insert the auto-PK is refreshed from LAST_INSERT_ID(); on a pure update the entity's existing PK value is preserved.

public Task<Result<T>> UpsertAsync(T entity, CancellationToken ct = default)

Parameters

entity T
ct CancellationToken

Returns

Task<Result<T>>

UpsertManyAsync(IEnumerable<T>, CancellationToken)

Bulk-upserts a collection of entities using batched INSERT ... ON DUPLICATE KEY UPDATE statements (set semantics). Batches of up to the constructor's maxBatchInsertSize (default 500) are sent per round-trip. Returns the total rows-affected count (MySQL counts 1 for each insert and 2 for each update, so this is not equal to entities.Count).

public Task<Result<int>> UpsertManyAsync(IEnumerable<T> entities, CancellationToken ct = default)

Parameters

entities IEnumerable<T>
ct CancellationToken

Returns

Task<Result<int>>

UpsertWithIncrementsAsync(T, IReadOnlyList<string>, IReadOnlyList<string>?, CancellationToken)

Inserts insertSeed if no UNIQUE/PRIMARY-KEY conflict occurs; otherwise applies increment / set semantics to the listed properties on conflict. Properties NOT listed in either array are insert-only — present in the VALUES clause but absent from ON DUPLICATE KEY UPDATE (so they don't change on conflict — useful for created_utc style columns). Property names resolve through CL.MySQL2.Core.EntityMetadata<T> so callers can use nameof(...) for compile-time-safe column references.

public Task<Result<int>> UpsertWithIncrementsAsync(T insertSeed, IReadOnlyList<string> incrementProperties, IReadOnlyList<string>? setProperties = null, CancellationToken ct = default)

Parameters

insertSeed T

Row to INSERT if no conflict. All non-auto-PK columns go into the VALUES clause.

incrementProperties IReadOnlyList<string>

C# property names whose columns should col = col + new.col on conflict.

setProperties IReadOnlyList<string>

C# property names whose columns should col = new.col on conflict. Defaults to empty.

ct CancellationToken

Cancellation token.

Returns

Task<Result<int>>

Rows affected (MySQL: 1 = insert, 2 = update with changes, 0 = update with no change).

Exceptions

ArgumentException

A name in incrementProperties or setProperties does not resolve to a property on T, refers to an auto-increment PK, or appears in both arrays.