Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ private static readonly MethodInfo TemporalPropertyHasColumnNameMethodInfo
= typeof(TemporalPeriodPropertyBuilder).GetRuntimeMethod(
nameof(TemporalPeriodPropertyBuilder.HasColumnName), [typeof(string)])!;

private static readonly MethodInfo TemporalPropertyIsHiddenMethodInfo
= typeof(TemporalPeriodPropertyBuilder).GetRuntimeMethod(
nameof(TemporalPeriodPropertyBuilder.IsHidden), [typeof(bool)])!;

private static readonly MethodInfo ModelHasFullTextCatalogMethodInfo
= typeof(SqlServerModelBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerModelBuilderExtensions.HasFullTextCatalog), [typeof(ModelBuilder), typeof(string)])!;
Expand Down Expand Up @@ -371,19 +375,17 @@ public override IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
: new MethodCallCodeFragment(TemporalTableUseHistoryTableMethodInfo2, historyTableName));
}

// ttb => ttb.HasPeriodStart("Start").HasColumnName("ColumnStart")
// ttb => ttb.HasPeriodStart("Start").HasColumnName("ColumnStart").IsHidden(false)
// IsHidden(false) is only chained when the user explicitly configured the column visible —
// the default is HIDDEN, so omitting matches the legacy snapshot output.
temporalTableBuilderCalls.Add(
periodStartColumnName != null
? new MethodCallCodeFragment(TemporalTableHasPeriodStartMethodInfo, periodStartPropertyName)
.Chain(new MethodCallCodeFragment(TemporalPropertyHasColumnNameMethodInfo, periodStartColumnName))
: new MethodCallCodeFragment(TemporalTableHasPeriodStartMethodInfo, periodStartPropertyName));
BuildPeriodPropertyCall(
TemporalTableHasPeriodStartMethodInfo, periodStartPropertyName, periodStartColumnName, periodStartProperty));

// ttb => ttb.HasPeriodEnd("End").HasColumnName("ColumnEnd")
// ttb => ttb.HasPeriodEnd("End").HasColumnName("ColumnEnd").IsHidden(false)
temporalTableBuilderCalls.Add(
periodEndColumnName != null
? new MethodCallCodeFragment(TemporalTableHasPeriodEndMethodInfo, periodEndPropertyName)
.Chain(new MethodCallCodeFragment(TemporalPropertyHasColumnNameMethodInfo, periodEndColumnName))
: new MethodCallCodeFragment(TemporalTableHasPeriodEndMethodInfo, periodEndPropertyName));
BuildPeriodPropertyCall(
TemporalTableHasPeriodEndMethodInfo, periodEndPropertyName, periodEndColumnName, periodEndProperty));

// ToTable(tb => tb.IsTemporal(ttb => { ... }))
var toTemporalTableCall = new MethodCallCodeFragment(
Expand All @@ -406,6 +408,27 @@ public override IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
}

return fragments;

static MethodCallCodeFragment BuildPeriodPropertyCall(
MethodInfo hasPeriodMethod,
string? periodPropertyName,
string? periodColumnName,
IReadOnlyProperty? periodProperty)
{
var call = new MethodCallCodeFragment(hasPeriodMethod, periodPropertyName);

if (periodColumnName != null)
{
call = call.Chain(new MethodCallCodeFragment(TemporalPropertyHasColumnNameMethodInfo, periodColumnName));
}

if (periodProperty?.IsHidden() == false)
{
call = call.Chain(new MethodCallCodeFragment(TemporalPropertyIsHiddenMethodInfo, false));
}

return call;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
annotations.Remove(SqlServerAnnotationNames.IdentityIncrement);
annotations.Remove(SqlServerAnnotationNames.IdentitySeed);
annotations.Remove(SqlServerAnnotationNames.Sparse);
annotations.Remove(SqlServerAnnotationNames.IsHidden);

if (!annotations.ContainsKey(SqlServerAnnotationNames.ValueGenerationStrategy))
{
Expand All @@ -88,6 +89,7 @@ public override void Generate(IColumn column, CSharpRuntimeAnnotationCodeGenerat
annotations.Remove(SqlServerAnnotationNames.Sparse);
annotations.Remove(SqlServerAnnotationNames.TemporalIsPeriodStartColumn);
annotations.Remove(SqlServerAnnotationNames.TemporalIsPeriodEndColumn);
annotations.Remove(SqlServerAnnotationNames.IsHidden);
}

base.Generate(column, parameters);
Expand Down Expand Up @@ -187,6 +189,8 @@ public override void Generate(ITable table, CSharpRuntimeAnnotationCodeGenerator
annotations.Remove(SqlServerAnnotationNames.TemporalHistoryTableSchema);
annotations.Remove(SqlServerAnnotationNames.TemporalPeriodEndColumnName);
annotations.Remove(SqlServerAnnotationNames.TemporalPeriodStartColumnName);
annotations.Remove(SqlServerAnnotationNames.TemporalPeriodStartHidden);
annotations.Remove(SqlServerAnnotationNames.TemporalPeriodEndHidden);
}

base.Generate(table, parameters);
Expand Down
18 changes: 18 additions & 0 deletions src/EFCore.SqlServer/EFCore.SqlServer.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalPeriodPropertyBuilder HasPrecision(int precision);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalPeriodPropertyBuilder IsHidden(bool hidden = true);"
},
{
"Member": "override string? ToString();"
}
Expand Down Expand Up @@ -2515,6 +2518,9 @@
{
"Member": "static Microsoft.EntityFrameworkCore.Metadata.ConfigurationSource? GetIdentitySeedConfigurationSource(this Microsoft.EntityFrameworkCore.Metadata.IConventionRelationalPropertyOverrides overrides);"
},
{
"Member": "static Microsoft.EntityFrameworkCore.Metadata.ConfigurationSource? GetIsHiddenConfigurationSource(this Microsoft.EntityFrameworkCore.Metadata.IConventionProperty property);"
},
{
"Member": "static Microsoft.EntityFrameworkCore.Metadata.ConfigurationSource? GetIsSparseConfigurationSource(this Microsoft.EntityFrameworkCore.Metadata.IConventionProperty property);"
},
Expand Down Expand Up @@ -2557,6 +2563,9 @@
{
"Member": "static bool IsCompatibleWithValueGeneration(Microsoft.EntityFrameworkCore.Metadata.IReadOnlyProperty property);"
},
{
"Member": "static bool? IsHidden(this Microsoft.EntityFrameworkCore.Metadata.IReadOnlyProperty property);"
},
{
"Member": "static bool? IsSparse(this Microsoft.EntityFrameworkCore.Metadata.IReadOnlyProperty property);"
},
Expand Down Expand Up @@ -2611,6 +2620,12 @@
{
"Member": "static long? SetIdentitySeed(this Microsoft.EntityFrameworkCore.Metadata.IConventionRelationalPropertyOverrides overrides, long? seed, bool fromDataAnnotation = false);"
},
{
"Member": "static void SetIsHidden(this Microsoft.EntityFrameworkCore.Metadata.IMutableProperty property, bool? hidden);"
},
{
"Member": "static bool? SetIsHidden(this Microsoft.EntityFrameworkCore.Metadata.IConventionProperty property, bool? hidden, bool fromDataAnnotation = false);"
},
{
"Member": "static void SetIsSparse(this Microsoft.EntityFrameworkCore.Metadata.IMutableProperty property, bool? sparse);"
},
Expand Down Expand Up @@ -3029,6 +3044,9 @@
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalPeriodPropertyBuilder HasPrecision(int precision);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalPeriodPropertyBuilder IsHidden(bool hidden = true);"
},
{
"Member": "override string? ToString();"
}
Expand Down
48 changes: 48 additions & 0 deletions src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,4 +1050,52 @@ public static void SetIsSparse(this IMutableProperty property, bool? sparse)
/// <returns>The <see cref="ConfigurationSource" /> for whether the property's column is sparse.</returns>
public static ConfigurationSource? GetIsSparseConfigurationSource(this IConventionProperty property)
=> property.FindAnnotation(SqlServerAnnotationNames.Sparse)?.GetConfigurationSource();

/// <summary>
/// Returns a value indicating whether the property's column is defined with the SQL Server <c>HIDDEN</c> flag,
/// which excludes the column from <c>SELECT *</c> results.
/// </summary>
/// <remarks>
/// This applies to columns defined with <c>GENERATED ALWAYS AS</c>, including SQL Server temporal table
/// period columns. The default for temporal period columns is <see langword="true" />; for other columns
/// this annotation has no effect unless the column is generated.
/// </remarks>
/// <param name="property">The property.</param>
/// <returns><see langword="true" /> if the property's column is hidden.</returns>
public static bool? IsHidden(this IReadOnlyProperty property)
=> (property is RuntimeProperty)
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (bool?)property[SqlServerAnnotationNames.IsHidden];

/// <summary>
/// Sets a value indicating whether the property's column is defined with the SQL Server <c>HIDDEN</c> flag.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="hidden">The value to set; <see langword="null" /> to remove the explicit configuration.</param>
public static void SetIsHidden(this IMutableProperty property, bool? hidden)
=> property.SetOrRemoveAnnotation(SqlServerAnnotationNames.IsHidden, hidden);

/// <summary>
/// Sets a value indicating whether the property's column is defined with the SQL Server <c>HIDDEN</c> flag.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="hidden">The value to set; <see langword="null" /> to remove the explicit configuration.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static bool? SetIsHidden(
this IConventionProperty property,
bool? hidden,
bool fromDataAnnotation = false)
=> (bool?)property.SetOrRemoveAnnotation(
SqlServerAnnotationNames.IsHidden,
hidden,
fromDataAnnotation)?.Value;

/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for whether the property's column is hidden.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The <see cref="ConfigurationSource" /> for whether the property's column is hidden.</returns>
public static ConfigurationSource? GetIsHiddenConfigurationSource(this IConventionProperty property)
=> property.FindAnnotation(SqlServerAnnotationNames.IsHidden)?.GetConfigurationSource();
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ public virtual OwnedNavigationTemporalPeriodPropertyBuilder HasPrecision(int pre
return this;
}

/// <summary>
/// Configures whether the period column is defined with the SQL Server <c>HIDDEN</c> flag,
/// which excludes it from <c>SELECT *</c> results.
/// </summary>
/// <remarks>
/// <para>
/// The default is <see langword="true" /> for period columns, matching the behavior of EF Core releases
/// prior to this option being introduced. Set to <see langword="false" /> to make the column visible.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information.
/// </para>
/// </remarks>
/// <param name="hidden">A value indicating whether the column should be hidden.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public virtual OwnedNavigationTemporalPeriodPropertyBuilder IsHidden(bool hidden = true)
{
((IMutableProperty)_propertyBuilder.Metadata).SetIsHidden(hidden);

return this;
}

#region Hidden System.Object members

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ public virtual TemporalPeriodPropertyBuilder HasPrecision(int precision)
return this;
}

/// <summary>
/// Configures whether the period column is defined with the SQL Server <c>HIDDEN</c> flag,
/// which excludes it from <c>SELECT *</c> results.
/// </summary>
/// <remarks>
/// <para>
/// The default is <see langword="true" /> for period columns, matching the behavior of EF Core releases
/// prior to this option being introduced. Set to <see langword="false" /> to make the column visible.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information.
/// </para>
/// </remarks>
/// <param name="hidden">A value indicating whether the column should be hidden.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public virtual TemporalPeriodPropertyBuilder IsHidden(bool hidden = true)
{
((IMutableProperty)_propertyBuilder.Metadata).SetIsHidden(hidden);

return this;
}

PropertyBuilder IInfrastructure<PropertyBuilder>.Instance
=> _propertyBuilder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,30 @@ public static class SqlServerAnnotationNames
/// </summary>
public const string TemporalIsPeriodEndColumn = Prefix + "TemporalIsPeriodEndColumn";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string IsHidden = Prefix + "IsHidden";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string TemporalPeriodStartHidden = Prefix + "TemporalPeriodStartHidden";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string TemporalPeriodEndHidden = Prefix + "TemporalPeriodEndHidden";

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ public override IEnumerable<IAnnotation> For(ITable table, bool designTime)
// see #26007
var storeObjectIdentifier = StoreObjectIdentifier.Table(table.Name, table.Schema);
var periodStartPropertyName = entityType.GetPeriodStartPropertyName();
IReadOnlyProperty? periodStartProperty = null;
if (periodStartPropertyName != null)
{
var periodStartProperty = entityType.FindProperty(periodStartPropertyName);
periodStartProperty = entityType.FindProperty(periodStartPropertyName);
var periodStartColumnName = periodStartProperty != null
? periodStartProperty.GetColumnName(storeObjectIdentifier)
: periodStartPropertyName;
Expand All @@ -121,15 +122,29 @@ public override IEnumerable<IAnnotation> For(ITable table, bool designTime)
}

var periodEndPropertyName = entityType.GetPeriodEndPropertyName();
IReadOnlyProperty? periodEndProperty = null;
if (periodEndPropertyName != null)
{
var periodEndProperty = entityType.FindProperty(periodEndPropertyName);
periodEndProperty = entityType.FindProperty(periodEndPropertyName);
var periodEndColumnName = periodEndProperty != null
? periodEndProperty.GetColumnName(storeObjectIdentifier)
: periodEndPropertyName;

yield return new Annotation(SqlServerAnnotationNames.TemporalPeriodEndColumnName, periodEndColumnName);
}

// Emit the per-period-column hidden flags on the table operation so the migrations generator
// can read them in BuildTemporalInformationFromMigrationOperation. Only emit when the user
// explicitly configured the column visible (default is HIDDEN, omitted to keep table ops clean).
if (periodStartProperty?.IsHidden() == false)
{
yield return new Annotation(SqlServerAnnotationNames.TemporalPeriodStartHidden, false);
}

if (periodEndProperty?.IsHidden() == false)
{
yield return new Annotation(SqlServerAnnotationNames.TemporalPeriodEndHidden, false);
}
}
}

Expand Down Expand Up @@ -351,10 +366,21 @@ public override IEnumerable<IAnnotation> For(IColumn column, bool designTime)
if (column.Name == periodStartColumnName)
{
yield return new Annotation(SqlServerAnnotationNames.TemporalIsPeriodStartColumn, true);

// Period columns default to HIDDEN; only emit the annotation when explicitly visible.
if (periodStartProperty?.IsHidden() == false)
{
yield return new Annotation(SqlServerAnnotationNames.IsHidden, false);
}
}
else if (column.Name == periodEndColumnName)
{
yield return new Annotation(SqlServerAnnotationNames.TemporalIsPeriodEndColumn, true);

if (periodEndProperty?.IsHidden() == false)
{
yield return new Annotation(SqlServerAnnotationNames.IsHidden, false);
}
}
}
}
Expand Down
Loading
Loading