Skip to content

Commit

Permalink
Switch to using mapping fragments for UseSqlOutputClause
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Jan 16, 2023
1 parent 1d76c26 commit 84c2033
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 182 deletions.
140 changes: 65 additions & 75 deletions src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,61 +282,63 @@ public static void SetHistoryTableSchema(this IMutableEntityType entityType, str

#region SQL OUTPUT clause

/// <summary>
/// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is
/// incompatible with certain SQL Server features, such as tables with triggers.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType)
{
if (entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause) is { Value: bool useSqlOutputClause } )
{
return useSqlOutputClause;
}

if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy
&& entityType.BaseType is not null)
{
return entityType.GetRootType().IsSqlOutputClauseUsed();
}

return true;
}

/// <summary>
/// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
/// with certain SQL Server features, such as tables with triggers.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <param name="useSqlOutputClause">The value to set.</param>
public static void UseSqlOutputClause(this IMutableEntityType entityType, bool? useSqlOutputClause)
=> entityType.SetOrRemoveAnnotation(SqlServerAnnotationNames.UseSqlOutputClause, useSqlOutputClause);

/// <summary>
/// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
/// with certain SQL Server features, such as tables with triggers.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <param name="useSqlOutputClause">The value to set.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static bool? UseSqlOutputClause(
this IConventionEntityType entityType,
bool? useSqlOutputClause,
bool fromDataAnnotation = false)
=> (bool?)entityType.SetOrRemoveAnnotation(
SqlServerAnnotationNames.UseSqlOutputClause,
useSqlOutputClause,
fromDataAnnotation)?.Value;

/// <summary>
/// Gets the configuration source for whether to use the SQL OUTPUT clause when saving changes to the table.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <returns>The configuration source for the memory-optimized setting.</returns>
public static ConfigurationSource? GetUseSqlOutputClauseConfigurationSource(this IConventionEntityType entityType)
=> entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause)?.GetConfigurationSource();
// /// <summary>
// /// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is
// /// incompatible with certain SQL Server features, such as tables with triggers.
// /// </summary>
// /// <param name="entityType">The entity type.</param>
// /// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
// public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType)
// {
// // TODO: Consult mapping fragments? But should we even have IsSqlOutputClauseUsed at the entity type level, not on a specific table?
//
// if (entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause) is { Value: bool useSqlOutputClause } )
// {
// return useSqlOutputClause;
// }
//
// if (entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy
// && entityType.BaseType is not null)
// {
// return entityType.GetRootType().IsSqlOutputClauseUsed();
// }
//
// return true;
// }
//
// /// <summary>
// /// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
// /// with certain SQL Server features, such as tables with triggers.
// /// </summary>
// /// <param name="entityType">The entity type.</param>
// /// <param name="useSqlOutputClause">The value to set.</param>
// public static void UseSqlOutputClause(this IMutableEntityType entityType, bool? useSqlOutputClause)
// => entityType.SetOrRemoveAnnotation(SqlServerAnnotationNames.UseSqlOutputClause, useSqlOutputClause);
//
// /// <summary>
// /// Sets a value indicating whether to use the SQL OUTPUT clause when saving changes to the table. The OUTPUT clause is incompatible
// /// with certain SQL Server features, such as tables with triggers.
// /// </summary>
// /// <param name="entityType">The entity type.</param>
// /// <param name="useSqlOutputClause">The value to set.</param>
// /// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
// /// <returns>The configured value.</returns>
// public static bool? UseSqlOutputClause(
// this IConventionEntityType entityType,
// bool? useSqlOutputClause,
// bool fromDataAnnotation = false)
// => (bool?)entityType.SetOrRemoveAnnotation(
// SqlServerAnnotationNames.UseSqlOutputClause,
// useSqlOutputClause,
// fromDataAnnotation)?.Value;
//
// /// <summary>
// /// Gets the configuration source for whether to use the SQL OUTPUT clause when saving changes to the table.
// /// </summary>
// /// <param name="entityType">The entity type.</param>
// /// <returns>The configuration source for the memory-optimized setting.</returns>
// public static ConfigurationSource? GetUseSqlOutputClauseConfigurationSource(this IConventionEntityType entityType)
// => entityType.FindAnnotation(SqlServerAnnotationNames.UseSqlOutputClause)?.GetConfigurationSource();

/// <summary>
/// Returns a value indicating whether to use the SQL OUTPUT clause when saving changes to the specified table.
Expand All @@ -346,39 +348,27 @@ public static void UseSqlOutputClause(this IMutableEntityType entityType, bool?
/// <param name="storeObject">The identifier of the table-like store object.</param>
/// <returns>A value indicating whether the SQL OUTPUT clause is used to save changes to the associated table.</returns>
public static bool IsSqlOutputClauseUsed(this IReadOnlyEntityType entityType, in StoreObjectIdentifier storeObject)
{
var overrides = entityType.FindMappingFragment(storeObject);
if (overrides != null)
{
return overrides.IsSqlOutputClauseUsed() ?? entityType.IsSqlOutputClauseUsed();
}

if (StoreObjectIdentifier.Create(entityType, storeObject.StoreObjectType) == storeObject)
{
return entityType.IsSqlOutputClauseUsed();
}

throw new InvalidOperationException(
RelationalStrings.TableNotMappedEntityType(entityType.DisplayName(), storeObject.DisplayName()));
}
=> entityType.FindMappingFragment(storeObject) is not { } overrides
|| overrides.IsSqlOutputClauseUsed() is not { } isSqlOutputClauseUsed
|| isSqlOutputClauseUsed;

/// <summary>
/// Sets a value indicating whether the associated table is ignored by Migrations.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <param name="excluded">A value indicating whether the associated table is ignored by Migrations.</param>
/// <param name="useSqlOutputClause">The value to set.</param>
/// <param name="storeObject">The identifier of the table-like store object.</param>
public static void UseSqlOutputClause(
this IMutableEntityType entityType,
bool? useSqlOutputClause,
in StoreObjectIdentifier storeObject)
=> entityType.GetOrCreateMappingFragment(storeObject).UseSqlOutputClause(excluded);
=> entityType.GetOrCreateMappingFragment(storeObject).UseSqlOutputClause(useSqlOutputClause);

/// <summary>
/// Sets a value indicating whether the associated table is ignored by Migrations.
/// </summary>
/// <param name="entityType">The entity type.</param>
/// <param name="excluded">A value indicating whether the associated table is ignored by Migrations.</param>
/// <param name="useSqlOutputClause">The value to set.</param>
/// <param name="storeObject">The identifier of the table-like store object.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
Expand All @@ -387,8 +377,8 @@ public static void UseSqlOutputClause(
bool? useSqlOutputClause,
in StoreObjectIdentifier storeObject,
bool fromDataAnnotation = false)
=> entityType.GetOrCreateMappingFragment(storeObject, fromDataAnnotation).SetIsTableExcludedFromMigrations(
excluded, fromDataAnnotation);
=> entityType.GetOrCreateMappingFragment(storeObject, fromDataAnnotation)
.UseSqlOutputClause(useSqlOutputClause, fromDataAnnotation);

#endregion SQL OUTPUT clause
}
38 changes: 24 additions & 14 deletions src/EFCore.SqlServer/Extensions/SqlServerTableBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,22 @@ public static TableBuilder UseSqlOutputClause(
this TableBuilder tableBuilder,
bool useSqlOutputClause = true)
{
tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
var entityType = tableBuilder.Metadata;

// TODO: TableBuilder has a (nullable) StoredObject, but it's protected and so cannot be accessed from here (should we make it public?)
// TODO: For IsExcludedFromMigration, the TableBuilder API seems to call the general API on entity type, without passing a
// StoreObjectIdentifier; that means it's not possible to flag only a specific table as excluded (although a metadata API for this
// does exist on RelationalEntityTypeExtensions, which isn't called anywhere)?

// TODO: Is this correct? Can GetTableName() return null given that this is a ToTable call?
if ((tableBuilder.Name ?? entityType.GetTableName()) is not { } tableName)
{
throw new InvalidOperationException("...");
}

entityType.UseSqlOutputClause(
useSqlOutputClause,
StoreObjectIdentifier.Table(tableName, tableBuilder.Schema));

return tableBuilder;
}
Expand All @@ -310,12 +325,7 @@ public static TableBuilder<TEntity> UseSqlOutputClause<TEntity>(
this TableBuilder<TEntity> tableBuilder,
bool useSqlOutputClause = true)
where TEntity : class
=> ((TableBuilder)tableBuilder).UseSqlOutputClause(tableBuilder, useSqlOutputClause);
// {
// tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
//
// return tableBuilder;
// }
=> (TableBuilder<TEntity>)((TableBuilder)tableBuilder).UseSqlOutputClause(useSqlOutputClause);

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

return tableBuilder;
// tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);
//
// return tableBuilder;
}

/// <summary>
Expand All @@ -355,11 +368,8 @@ public static OwnedNavigationTableBuilder<TOwnerEntity, TDependentEntity> UseSql
bool useSqlOutputClause = true)
where TOwnerEntity : class
where TDependentEntity : class
{
tableBuilder.Metadata.UseSqlOutputClause(useSqlOutputClause);

return tableBuilder;
}
=> (OwnedNavigationTableBuilder<TOwnerEntity, TDependentEntity>)
((OwnedNavigationTableBuilder)tableBuilder).UseSqlOutputClause(useSqlOutputClause);

#endregion UseSqlOutputClause
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ public static class SqlServerTableExtensions
/// <param name="table">The table.</param>
/// <returns><see langword="true" /> if the SQL OUTPUT clause is used to save changes to the table.</returns>
public static bool IsSqlOutputClauseUsed(this ITable table)
=> table.EntityTypeMappings.First().EntityType.IsSqlOutputClauseUsed(StoreObjectIdentifier.Table(table.Name, table.Schema));
=> table.EntityTypeMappings.All(e => e.EntityType.IsSqlOutputClauseUsed(StoreObjectIdentifier.Table(table.Name, table.Schema)));
}
Loading

0 comments on commit 84c2033

Please sign in to comment.