Class MergeBuilder<TEntity>
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
TEntityThe 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
filterKeysIEnumerable<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
filterKeysIEnumerable<string>The filter keys to disable.
entityTypesType[]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
entityTypesType[]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
cancellationTokenCancellationTokenCancels execution.
Returns
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
conditionExpression<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
TEntitycannot 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
projectionExpression<Func<TEntity, TResult>>Selects the mapped columns to return.
Returns
- MergeReturningBuilder<TEntity, TResult>
A returning builder whose terminals produce
TResult.
Type Parameters
TResultThe 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
sourceEntityBuilder<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
sourceQueryCommand<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
entitiesIEnumerable<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
entityTEntityThe 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
conditionExpression<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
assignmentsExpression<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
assignmentsExpression<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
conditionExpression<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
conditionExpression<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
assignmentsExpression<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.