Table of Contents

Class MergeBuilder<TEntity>

Namespace
NextORM.Core
Assembly
nextorm.core.dll

Fluent builder for a key upsert (a "merge" of a source row set into the target table), started with CreateMergeBuilder<TEntity>(IDataContext, Action<EntityMetadataBuilder<TEntity>>?). The source is a mapped entity or a batch of entities; the database decides, per key, whether to update the existing row or insert a new one. The statement is rendered through the active dialect as INSERT ... ON CONFLICT ... DO UPDATE (PostgreSQL, SQLite), INSERT ... ON DUPLICATE KEY UPDATE (MySQL, MariaDB) or MERGE (SQL Server).

There is deliberately no change tracking: every terminal (Merge(), MergeAsync(CancellationToken)) issues one explicit command, as with the insert builder. The builder is single-use.

public sealed class MergeBuilder<TEntity>

Type Parameters

TEntity

The mapped entity type upserted.

Inheritance
MergeBuilder<TEntity>
Inherited Members
Extension Methods

Remarks

A key upsert is defined by the Using(TEntity) source, the OnKeys() match key and both branches WhenMatchedUpdate() and WhenNotMatchedInsert(); all four are required. OnKeys() resolves the key from the entity mapping, so it must be declared before the merge; some providers (MySQL, MariaDB) render the update without naming the key because their native ON DUPLICATE KEY UPDATE clause has no key list.

Methods

IgnoreFilters()

Disables all global query filters declared for the target entity type for this statement: the merge target is no longer constrained or refused by them and the inserted side is not validated against them. Repeatable: a later call accumulates with the earlier scope.

public MergeBuilder<TEntity> IgnoreFilters()

Returns

MergeBuilder<TEntity>

This builder, for chaining.

IgnoreFilters(IEnumerable<string>)

Disables the named global query filters identified by filterKeys on the target entity type for this statement: the merge target is no longer constrained or refused by them and the inserted side is not validated against them. An empty or null filterKeys disables nothing. Repeatable: a later call accumulates (union) with the earlier scope.

public MergeBuilder<TEntity> IgnoreFilters(IEnumerable<string> filterKeys)

Parameters

filterKeys IEnumerable<string>

The filter keys to disable.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

IgnoreFilters(IEnumerable<string>, params Type[])

Disables the named global query filters identified by filterKeys only on the given entityTypes (the intersection of keys and types); an empty entityTypes means any entity type. The disabled filters no longer constrain, refuse or validate this merge's target. The key list is the gate: an empty or null filterKeys disables nothing even when entity types are supplied. Repeatable: a later call accumulates (union) with the earlier scope.

public MergeBuilder<TEntity> IgnoreFilters(IEnumerable<string> filterKeys, params Type[] entityTypes)

Parameters

filterKeys IEnumerable<string>

The filter keys to disable.

entityTypes Type[]

The entity types the disable is scoped to; empty means any entity type.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

IgnoreFilters(params Type[])

Disables every global query filter declared for the given entity types for this statement: the merge target is no longer constrained or refused by them and the inserted side is not validated against them. An empty or null entityTypes disables nothing. Repeatable: a later call accumulates (union) with the earlier scope.

public MergeBuilder<TEntity> IgnoreFilters(params Type[] entityTypes)

Parameters

entityTypes Type[]

The entity types whose filters are disabled.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Merge()

Executes the merge and returns the number of affected rows.

public int Merge()

Returns

int

The number of rows inserted or updated, as reported by the provider.

MergeAsync(CancellationToken)

Asynchronously executes the merge and returns the number of affected rows.

public Task<int> MergeAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

Cancels execution.

Returns

Task<int>

A task producing the number of rows inserted or updated.

On(Expression<Func<TEntity, TEntity, bool>>)

Matches existing rows on an arbitrary condition over the (target, source) row instead of the declared key. Only the full-MERGE branch form renders it, on providers that support a search condition (SupportsMergeConditionalBranches); the others reject it.

public MergeBuilder<TEntity> On(Expression<Func<TEntity, TEntity, bool>> condition)

Parameters

condition Expression<Func<TEntity, TEntity, bool>>

The match condition, for example (t, s) => t.Id == s.Id && s.Age > 0.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

InvalidOperationException

A match key was already declared with OnKeys().

OnKeys()

Matches existing rows on the entity's declared key column(s), resolved from metadata ([Key], the fluent .Key() or the Id/<Type>Id convention).

public MergeBuilder<TEntity> OnKeys()

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

InvalidOperationException

The entity has no key property.

NotSupportedException

A key column is database-generated.

Returning()

Switches the builder to a row-returning terminal that materialises the whole merged row through the provider's OUTPUT/RETURNING form. Both the full-MERGE branch form and the key-upsert form (ON CONFLICT ... DO UPDATE, MERGE) are supported where the provider has a row-returning clause; MySQL/MariaDB reject the key-upsert form.

public MergeReturningBuilder<TEntity, TEntity> Returning()

Returns

MergeReturningBuilder<TEntity, TEntity>

A returning builder whose terminals produce TEntity.

Exceptions

NotSupportedException

A mapped property of TEntity cannot be projected.

Returning<TResult>(Expression<Func<TEntity, TResult>>)

Switches the builder to a row-returning terminal that materialises a projection of the merged row through the provider's OUTPUT/RETURNING form. Both the full-MERGE branch form and the key-upsert form are supported where the provider has a row-returning clause.

public MergeReturningBuilder<TEntity, TResult> Returning<TResult>(Expression<Func<TEntity, TResult>> projection)

Parameters

projection Expression<Func<TEntity, TResult>>

Selects the mapped columns to return.

Returns

MergeReturningBuilder<TEntity, TResult>

A returning builder whose terminals produce TResult.

Type Parameters

TResult

The projected row shape.

Exceptions

NotSupportedException

The projection references something other than mapped properties.

ToSql()

Renders the parameterised SQL this builder would execute, without executing it. Useful for diagnostics and for verifying SQL generation without a database.

public string ToSql()

Returns

string

The rendered SQL text.

Using(EntityBuilder<TEntity>)

Uses a server-side query as the source rows (MERGE ... USING (<select>) AS source). The query is an entity query over TEntity, so its output columns line up with the target. Only the full-MERGE branch form is supported for a query source.

public MergeBuilder<TEntity> Using(EntityBuilder<TEntity> source)

Parameters

source EntityBuilder<TEntity>

The entity query supplying the source rows.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

InvalidOperationException

The source was already specified.

Using(QueryCommand<TEntity>)

Uses a server-side query command as the source rows.

public MergeBuilder<TEntity> Using(QueryCommand<TEntity> source)

Parameters

source QueryCommand<TEntity>

The entity query command supplying the source rows.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

InvalidOperationException

The source was already specified.

Using(IEnumerable<TEntity>)

Uses a batch of mapped entities as the source rows. Identity and computed columns are excluded automatically.

public MergeBuilder<TEntity> Using(IEnumerable<TEntity> entities)

Parameters

entities IEnumerable<TEntity>

The entities whose column values form the source rows.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

InvalidOperationException

The source was already specified.

Using(TEntity)

Uses a single mapped entity as the source row.

public MergeBuilder<TEntity> Using(TEntity entity)

Parameters

entity TEntity

The entity whose column values form the source row.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

WhenMatched()

Introduces a full-MERGE WHEN MATCHED branch; finish it with ThenUpdate()/ThenDelete(). Available only on providers that render a general MERGE (SQL Server, PostgreSQL 15+); the other dialects reject it with NotSupportedException.

public MergeMatchedBuilder<TEntity> WhenMatched()

Returns

MergeMatchedBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenMatched(Expression<Func<TEntity, TEntity, bool>>)

Introduces a conditional full-MERGE WHEN MATCHED branch (WHEN MATCHED AND <condition>); finish it with ThenUpdate()/ThenDelete()/ThenDoNothing().

public MergeMatchedBuilder<TEntity> WhenMatched(Expression<Func<TEntity, TEntity, bool>> condition)

Parameters

condition Expression<Func<TEntity, TEntity, bool>>

The branch condition over (target, source), for example (t, s) => t.Name != s.Name.

Returns

MergeMatchedBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenMatchedUpdate()

Updates every non-key writable column from the source when the row already exists.

public MergeBuilder<TEntity> WhenMatchedUpdate()

Returns

MergeBuilder<TEntity>

This builder, for chaining.

WhenMatchedUpdate(Expression<Func<TEntity, TEntity, object?>>)

Updates only the explicitly assigned columns when the row already exists. The lambda body must be an anonymous initializer or a named initializer assignable to the entity; the values may read the pre-update target row and the original input row. This is the expression form of the key upsert; combination with the no-argument WhenNotMatchedInsert() keeps the implicit key binding.

public MergeBuilder<TEntity> WhenMatchedUpdate(Expression<Func<TEntity, TEntity, object?>> assignments)

Parameters

assignments Expression<Func<TEntity, TEntity, object>>

The assignments, for example (target, source) => new { Count = target.Count + source.Count }.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

ArgumentException

The body is not a supported non-empty initializer, or a destination is unknown, duplicated or type-incompatible.

NotSupportedException

A destination may not be written, or the provider cannot express the form.

WhenMatchedUpdate(Expression<Func<TEntity, TEntity, TEntity, object?>>)

Updates only the explicitly assigned columns when the row already exists, with the third parameter naming the provider-native proposed insert row (excluded). A parameter that is never read is accepted; reading it on a provider without a native proposed row fails before execution.

public MergeBuilder<TEntity> WhenMatchedUpdate(Expression<Func<TEntity, TEntity, TEntity, object?>> assignments)

Parameters

assignments Expression<Func<TEntity, TEntity, TEntity, object>>

The assignments, for example (target, source, excluded) => new { Count = target.Count + excluded.Count }.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

ArgumentException

The body is not a supported non-empty initializer, or a destination is unknown, duplicated or type-incompatible.

NotSupportedException

A destination may not be written, or the provider has no proposed row for this form.

WhenNotMatched()

Introduces a full-MERGE WHEN NOT MATCHED [BY TARGET] branch; finish it with ThenInsert(). Available only on providers that render a general MERGE.

public MergeNotMatchedBuilder<TEntity> WhenNotMatched()

Returns

MergeNotMatchedBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenNotMatched(Expression<Func<TEntity, TEntity, bool>>)

Introduces a conditional full-MERGE WHEN NOT MATCHED [BY TARGET] branch (WHEN NOT MATCHED AND <condition>); finish it with ThenInsert()/ThenDoNothing().

public MergeNotMatchedBuilder<TEntity> WhenNotMatched(Expression<Func<TEntity, TEntity, bool>> condition)

Parameters

condition Expression<Func<TEntity, TEntity, bool>>

The branch condition over (target, source).

Returns

MergeNotMatchedBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenNotMatchedBySource()

Introduces a full-MERGE WHEN NOT MATCHED BY SOURCE branch; finish it with ThenDelete(). Available only on SQL Server, the sole supported provider that renders this branch; every other dialect rejects it with NotSupportedException.

public MergeNotMatchedBySourceBuilder<TEntity> WhenNotMatchedBySource()

Returns

MergeNotMatchedBySourceBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenNotMatchedBySource(Expression<Func<TEntity, TEntity, bool>>)

Introduces a conditional full-MERGE WHEN NOT MATCHED BY SOURCE branch (WHEN NOT MATCHED BY SOURCE AND <condition>); finish it with ThenDelete()/ThenDoNothing(). SQL Server only allows the condition to reference the target row.

public MergeNotMatchedBySourceBuilder<TEntity> WhenNotMatchedBySource(Expression<Func<TEntity, TEntity, bool>> condition)

Parameters

condition Expression<Func<TEntity, TEntity, bool>>

The branch condition over (target, source).

Returns

MergeNotMatchedBySourceBuilder<TEntity>

A branch builder whose terminal returns this merge builder.

WhenNotMatchedInsert()

Inserts a new row when no key matches.

public MergeBuilder<TEntity> WhenNotMatchedInsert()

Returns

MergeBuilder<TEntity>

This builder, for chaining.

WhenNotMatchedInsert(Expression<Func<TEntity, object?>>)

Inserts a new row built from the explicitly assigned columns when no key matches. Only the listed columns are inserted, so omitted columns take their database defaults. Every configured conflict key must be assigned directly from the same-named original source property (Id = source.Id); a missing, constant, transformed or foreign-property key is rejected before execution and no key is injected implicitly.

public MergeBuilder<TEntity> WhenNotMatchedInsert(Expression<Func<TEntity, object?>> assignments)

Parameters

assignments Expression<Func<TEntity, object>>

The assignments, for example source => new { Id = source.Id, Name = source.Name }.

Returns

MergeBuilder<TEntity>

This builder, for chaining.

Exceptions

ArgumentException

The body is not a supported non-empty initializer, a destination is unknown, duplicated or type-incompatible, or a conflict key is not bound directly from its source property.

NotSupportedException

A destination may not be written, or an unavailable role is referenced.