Skip to content

Commit 84c2033

Browse files
committed
Switch to using mapping fragments for UseSqlOutputClause
1 parent 1d76c26 commit 84c2033

File tree

6 files changed

+269
-182
lines changed

6 files changed

+269
-182
lines changed

src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs

Lines changed: 65 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -282,61 +282,63 @@ public static void SetHistoryTableSchema(this IMutableEntityType entityType, str
282282

283283
#region SQL OUTPUT clause
284284

285-
/// <summary>
286-
/// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is
287-
/// incompatible with certain SQL Server features, such as tables with triggers.
288-
/// </summary>
289-
/// <param name="entityType">The entity type.</param>
290-
/// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
291-
public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType)
292-
{
293-
if (entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause) is { Value: bool useSqlOutputClause } )
294-
{
295-
return useSqlOutputClause;
296-
}
297-
298-
if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy
299-
&& entityType.BaseType is not null)
300-
{
301-
return entityType.GetRootType().IsSqlOutputClauseUsed();
302-
}
303-
304-
return true;
305-
}
306-
307-
/// <summary>
308-
/// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
309-
/// with certain SQL Server features, such as tables with triggers.
310-
/// </summary>
311-
/// <param name="entityType">The entity type.</param>
312-
/// <param name="useSqlOutputClause">The value to set.</param>
313-
public static void UseSqlOutputClause(this IMutableEntityType entityType, bool? useSqlOutputClause)
314-
=> entityType.SetOrRemoveAnnotation(SqlServerAnnotationNames.UseSqlOutputClause, useSqlOutputClause);
315-
316-
/// <summary>
317-
/// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
318-
/// with certain SQL Server features, such as tables with triggers.
319-
/// </summary>
320-
/// <param name="entityType">The entity type.</param>
321-
/// <param name="useSqlOutputClause">The value to set.</param>
322-
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
323-
/// <returns>The configured value.</returns>
324-
public static bool? UseSqlOutputClause(
325-
this IConventionEntityType entityType,
326-
bool? useSqlOutputClause,
327-
bool fromDataAnnotation = false)
328-
=> (bool?)entityType.SetOrRemoveAnnotation(
329-
SqlServerAnnotationNames.UseSqlOutputClause,
330-
useSqlOutputClause,
331-
fromDataAnnotation)?.Value;
332-
333-
/// <summary>
334-
/// Gets the configuration source for whether to use the SQL OUTPUT clause when saving changes to the table.
335-
/// </summary>
336-
/// <param name="entityType">The entity type.</param>
337-
/// <returns>The configuration source for the memory-optimized setting.</returns>
338-
public static ConfigurationSource? GetUseSqlOutputClauseConfigurationSource(this IConventionEntityType entityType)
339-
=> entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause)?.GetConfigurationSource();
285+
// /// <summary>
286+
// /// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is
287+
// /// incompatible with certain SQL Server features, such as tables with triggers.
288+
// /// </summary>
289+
// /// <param name="entityType">The entity type.</param>
290+
// /// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
291+
// public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType)
292+
// {
293+
// // TODO: Consult mapping fragments? But should we even have IsSqlOutputClauseUsed at the entity type level, not on a specific table?
294+
//
295+
// if (entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause) is { Value: bool useSqlOutputClause } )
296+
// {
297+
// return useSqlOutputClause;
298+
// }
299+
//
300+
// if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy
301+
// && entityType.BaseType is not null)
302+
// {
303+
// return entityType.GetRootType().IsSqlOutputClauseUsed();
304+
// }
305+
//
306+
// return true;
307+
// }
308+
//
309+
// /// <summary>
310+
// /// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
311+
// /// with certain SQL Server features, such as tables with triggers.
312+
// /// </summary>
313+
// /// <param name="entityType">The entity type.</param>
314+
// /// <param name="useSqlOutputClause">The value to set.</param>
315+
// public static void UseSqlOutputClause(this IMutableEntityType entityType, bool? useSqlOutputClause)
316+
// => entityType.SetOrRemoveAnnotation(SqlServerAnnotationNames.UseSqlOutputClause, useSqlOutputClause);
317+
//
318+
// /// <summary>
319+
// /// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
320+
// /// with certain SQL Server features, such as tables with triggers.
321+
// /// </summary>
322+
// /// <param name="entityType">The entity type.</param>
323+
// /// <param name="useSqlOutputClause">The value to set.</param>
324+
// /// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
325+
// /// <returns>The configured value.</returns>
326+
// public static bool? UseSqlOutputClause(
327+
// this IConventionEntityType entityType,
328+
// bool? useSqlOutputClause,
329+
// bool fromDataAnnotation = false)
330+
// => (bool?)entityType.SetOrRemoveAnnotation(
331+
// SqlServerAnnotationNames.UseSqlOutputClause,
332+
// useSqlOutputClause,
333+
// fromDataAnnotation)?.Value;
334+
//
335+
// /// <summary>
336+
// /// Gets the configuration source for whether to use the SQL OUTPUT clause when saving changes to the table.
337+
// /// </summary>
338+
// /// <param name="entityType">The entity type.</param>
339+
// /// <returns>The configuration source for the memory-optimized setting.</returns>
340+
// public static ConfigurationSource? GetUseSqlOutputClauseConfigurationSource(this IConventionEntityType entityType)
341+
// => entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause)?.GetConfigurationSource();
340342

341343
/// <summary>
342344
/// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the specified table.
@@ -346,39 +348,27 @@ public static void UseSqlOutputClause(this IMutableEntityType entityType, bool?
346348
/// <param name="storeObject">The identifier of the table-like store object.</param>
347349
/// <returns>A value indicating whether the SQL OUTPUT clause is used to save changes to the associated table.</returns>
348350
public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType, in StoreObjectIdentifier storeObject)
349-
{
350-
var overrides = entityType.FindMappingFragment(storeObject);
351-
if (overrides != null)
352-
{
353-
return overrides.IsSqlOutputClauseUsed() ?? entityType.IsSqlOutputClauseUsed();
354-
}
355-
356-
if (StoreObjectIdentifier.Create(entityType, storeObject.StoreObjectType) == storeObject)
357-
{
358-
return entityType.IsSqlOutputClauseUsed();
359-
}
360-
361-
throw new InvalidOperationException(
362-
RelationalStrings.TableNotMappedEntityType(entityType.DisplayName(), storeObject.DisplayName()));
363-
}
351+
=> entityType.FindMappingFragment(storeObject) is not { } overrides
352+
|| overrides.IsSqlOutputClauseUsed() is not { } isSqlOutputClauseUsed
353+
|| isSqlOutputClauseUsed;
364354

365355
/// <summary>
366356
/// Sets a value indicating whether the associated table is ignored by Migrations.
367357
/// </summary>
368358
/// <param name="entityType">The entity type.</param>
369-
/// <param name="excluded">A value indicating whether the associated table is ignored by Migrations.</param>
359+
/// <param name="useSqlOutputClause">The value to set.</param>
370360
/// <param name="storeObject">The identifier of the table-like store object.</param>
371361
public static void UseSqlOutputClause(
372362
this IMutableEntityType entityType,
373363
bool? useSqlOutputClause,
374364
in StoreObjectIdentifier storeObject)
375-
=> entityType.GetOrCreateMappingFragment(storeObject).UseSqlOutputClause(excluded);
365+
=> entityType.GetOrCreateMappingFragment(storeObject).UseSqlOutputClause(useSqlOutputClause);
376366

377367
/// <summary>
378368
/// Sets a value indicating whether the associated table is ignored by Migrations.
379369
/// </summary>
380370
/// <param name="entityType">The entity type.</param>
381-
/// <param name="excluded">A value indicating whether the associated table is ignored by Migrations.</param>
371+
/// <param name="useSqlOutputClause">The value to set.</param>
382372
/// <param name="storeObject">The identifier of the table-like store object.</param>
383373
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
384374
/// <returns>The configured value.</returns>
@@ -387,8 +377,8 @@ public static void UseSqlOutputClause(
387377
bool? useSqlOutputClause,
388378
in StoreObjectIdentifier storeObject,
389379
bool fromDataAnnotation = false)
390-
=> entityType.GetOrCreateMappingFragment(storeObject, fromDataAnnotation).SetIsTableExcludedFromMigrations(
391-
excluded, fromDataAnnotation);
380+
=> entityType.GetOrCreateMappingFragment(storeObject, fromDataAnnotation)
381+
.UseSqlOutputClause(useSqlOutputClause, fromDataAnnotation);
392382

393383
#endregion SQL OUTPUT clause
394384
}

src/EFCore.SqlServer/Extensions/SqlServerTableBuilderExtensions.cs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,22 @@ public static TableBuilder UseSqlOutputClause(
289289
this TableBuilder tableBuilder,
290290
bool useSqlOutputClause = true)
291291
{
292-
tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
292+
var entityType = tableBuilder.Metadata;
293+
294+
// TODO: TableBuilder has a (nullable) StoredObject, but it's protected and so cannot be accessed from here (should we make it public?)
295+
// TODO: For IsExcludedFromMigration, the TableBuilder API seems to call the general API on entity type, without passing a
296+
// StoreObjectIdentifier; that means it's not possible to flag only a specific table as excluded (although a metadata API for this
297+
// does exist on RelationalEntityTypeExtensions, which isn't called anywhere)?
298+
299+
// TODO: Is this correct? Can GetTableName() return null given that this is a ToTable call?
300+
if ((tableBuilder.Name ?? entityType.GetTableName()) is not { } tableName)
301+
{
302+
throw new InvalidOperationException("...");
303+
}
304+
305+
entityType.UseSqlOutputClause(
306+
useSqlOutputClause,
307+
StoreObjectIdentifier.Table(tableName, tableBuilder.Schema));
293308

294309
return tableBuilder;
295310
}
@@ -310,12 +325,7 @@ public static TableBuilder<TEntity> UseSqlOutputClause<TEntity>(
310325
this TableBuilder<TEntity> tableBuilder,
311326
bool useSqlOutputClause = true)
312327
where TEntity : class
313-
=> ((TableBuilder)tableBuilder).UseSqlOutputClause(tableBuilder, useSqlOutputClause);
314-
// {
315-
// tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
316-
//
317-
// return tableBuilder;
318-
// }
328+
=> (TableBuilder<TEntity>)((TableBuilder)tableBuilder).UseSqlOutputClause(useSqlOutputClause);
319329

320330
/// <summary>
321331
/// Configures whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible with
@@ -332,9 +342,12 @@ public static OwnedNavigationTableBuilder UseSqlOutputClause(
332342
this OwnedNavigationTableBuilder tableBuilder,
333343
bool useSqlOutputClause = true)
334344
{
335-
tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
345+
// TODO: Do the same as for TableBuilder above
346+
throw new NotImplementedException();
336347

337-
return tableBuilder;
348+
// tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
349+
//
350+
// return tableBuilder;
338351
}
339352

340353
/// <summary>
@@ -355,11 +368,8 @@ public static OwnedNavigationTableBuilder<TOwnerEntity, TDependentEntity> UseSql
355368
bool useSqlOutputClause = true)
356369
where TOwnerEntity : class
357370
where TDependentEntity : class
358-
{
359-
tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
360-
361-
return tableBuilder;
362-
}
371+
=> (OwnedNavigationTableBuilder<TOwnerEntity, TDependentEntity>)
372+
((OwnedNavigationTableBuilder)tableBuilder).UseSqlOutputClause(useSqlOutputClause);
363373

364374
#endregion UseSqlOutputClause
365375
}

src/EFCore.SqlServer/Extensions/SqlServerTableExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ public static class SqlServerTableExtensions
1616
/// <param name="table">The table.</param>
1717
/// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
1818
public static bool IsSqlOutputClauseUsed(this ITable table)
19-
=> table.EntityTypeMappings.First().EntityType.IsSqlOutputClauseUsed(StoreObjectIdentifier.Table(table.Name, table.Schema));
19+
=> table.EntityTypeMappings.All(e => e.EntityType.IsSqlOutputClauseUsed(StoreObjectIdentifier.Table(table.Name, table.Schema)));
2020
}

0 commit comments

Comments
 (0)