Skip to content
Merged
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 @@ -338,7 +338,7 @@ public override IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
: null;

// for the RevEng path, we avoid adding period properties to the entity
// because we don't want code for them to be generated - they need to be in shadow state
// because we don't want code for them to be generated - they are created as shadow properties
// so if we don't find property on the entity, we know it's this scenario
// and in that case period column name is actually the same as the period property name annotation
// since in RevEng scenario there can't be custom column mapping
Expand Down
14 changes: 13 additions & 1 deletion src/EFCore.SqlServer/EFCore.SqlServer.baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@
{
"Type": "class Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity> : Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder where TOwnerEntity : class where TDependentEntity : class",
"Methods": [
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalPeriodPropertyBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity>.HasPeriodEnd(System.Linq.Expressions.Expression<System.Func<TDependentEntity, System.DateTime>> propertyExpression);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalPeriodPropertyBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity>.HasPeriodStart(System.Linq.Expressions.Expression<System.Func<TDependentEntity, System.DateTime>> propertyExpression);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity> Microsoft.EntityFrameworkCore.Metadata.Builders.OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity>.UseHistoryTable(string name);"
},
Expand Down Expand Up @@ -2630,7 +2636,7 @@
"Member": "static System.Linq.IQueryable<Microsoft.EntityFrameworkCore.Query.FullTextSearchResult<TKey>> Microsoft.EntityFrameworkCore.SqlServerQueryableExtensions.FreeTextTable<T, TKey>(this Microsoft.EntityFrameworkCore.DbSet<T> source, string freeText, System.Linq.Expressions.Expression<System.Func<T, object>>? columnSelector = null, string? languageTerm = null, int? topN = null);"
},
{
"Member": "static System.Linq.IQueryable<Microsoft.EntityFrameworkCore.VectorSearchResult<T>> Microsoft.EntityFrameworkCore.SqlServerQueryableExtensions.VectorSearch<T, TVector>(this Microsoft.EntityFrameworkCore.DbSet<T> source, System.Linq.Expressions.Expression<System.Func<T, TVector>> vectorPropertySelector, TVector similarTo, string metric, int topN);",
"Member": "static System.Linq.IQueryable<Microsoft.EntityFrameworkCore.VectorSearchResult<T>> Microsoft.EntityFrameworkCore.SqlServerQueryableExtensions.VectorSearch<T, TVector>(this Microsoft.EntityFrameworkCore.DbSet<T> source, System.Linq.Expressions.Expression<System.Func<T, TVector>> vectorPropertySelector, TVector similarTo, string metric);",
"Stage": "Experimental"
}
Comment thread
AndriySvyryd marked this conversation as resolved.
]
Expand Down Expand Up @@ -3030,6 +3036,12 @@
{
"Type": "class Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder<TEntity> : Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder where TEntity : class",
"Methods": [
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalPeriodPropertyBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder<TEntity>.HasPeriodEnd(System.Linq.Expressions.Expression<System.Func<TEntity, System.DateTime>> propertyExpression);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalPeriodPropertyBuilder Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder<TEntity>.HasPeriodStart(System.Linq.Expressions.Expression<System.Func<TEntity, System.DateTime>> propertyExpression);"
},
{
"Member": "virtual Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder<TEntity> Microsoft.EntityFrameworkCore.Metadata.Builders.TemporalTableBuilder<TEntity>.UseHistoryTable(string name);"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,6 @@ private static void ValidateTemporalPeriodProperty(IEntityType temporalEntityTyp
temporalEntityType.DisplayName(), annotationPropertyName));
}

if (!periodProperty.IsShadowProperty() && !temporalEntityType.IsPropertyBag)
{
throw new InvalidOperationException(
SqlServerStrings.TemporalPeriodPropertyMustBeInShadowState(
temporalEntityType.DisplayName(), periodProperty.Name));
}

if (periodProperty.IsNullable
|| periodProperty.ClrType != typeof(DateTime))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,36 @@ public OwnedNavigationTemporalTableBuilder(OwnedNavigationBuilder referenceOwner
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public new virtual OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity> UseHistoryTable(string name, string? schema)
=> (OwnedNavigationTemporalTableBuilder<TOwnerEntity, TDependentEntity>)base.UseHistoryTable(name, schema);

/// <summary>
/// Returns an object that can be used to configure a period start property of the entity type mapped to a temporal table.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured as the period start property
/// (<c>entity => entity.PeriodStart</c>).
/// </param>
/// <returns>An object that can be used to configure the period start property.</returns>
public virtual OwnedNavigationTemporalPeriodPropertyBuilder HasPeriodStart(
Expression<Func<TDependentEntity, DateTime>> propertyExpression)
=> HasPeriodStart(Check.NotNull(propertyExpression).GetMemberAccess().Name);

/// <summary>
/// Returns an object that can be used to configure a period end property of the entity type mapped to a temporal table.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured as the period end property
/// (<c>entity => entity.PeriodEnd</c>).
/// </param>
/// <returns>An object that can be used to configure the period end property.</returns>
public virtual OwnedNavigationTemporalPeriodPropertyBuilder HasPeriodEnd(
Expression<Func<TDependentEntity, DateTime>> propertyExpression)
=> HasPeriodEnd(Check.NotNull(propertyExpression).GetMemberAccess().Name);
}
30 changes: 30 additions & 0 deletions src/EFCore.SqlServer/Metadata/Builders/TemporalTableBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,34 @@ public TemporalTableBuilder(EntityTypeBuilder entityTypeBuilder)
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public new virtual TemporalTableBuilder<TEntity> UseHistoryTable(string name, string? schema)
=> (TemporalTableBuilder<TEntity>)base.UseHistoryTable(name, schema);

/// <summary>
/// Returns an object that can be used to configure a period start property of the entity type mapped to a temporal table.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured as the period start property
/// (<c>blog => blog.PeriodStart</c>).
/// </param>
/// <returns>An object that can be used to configure the period start property.</returns>
public virtual TemporalPeriodPropertyBuilder HasPeriodStart(Expression<Func<TEntity, DateTime>> propertyExpression)
=> HasPeriodStart(Check.NotNull(propertyExpression).GetMemberAccess().Name);

/// <summary>
/// Returns an object that can be used to configure a period end property of the entity type mapped to a temporal table.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-temporal">Using SQL Server temporal tables with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured as the period end property
/// (<c>blog => blog.PeriodEnd</c>).
/// </param>
/// <returns>An object that can be used to configure the period end property.</returns>
public virtual TemporalPeriodPropertyBuilder HasPeriodEnd(Expression<Func<TEntity, DateTime>> propertyExpression)
=> HasPeriodEnd(Check.NotNull(propertyExpression).GetMemberAccess().Name);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/EFCore.SqlServer/Properties/SqlServerStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,6 @@
<data name="TemporalPeriodPropertyCantHaveDefaultValue" xml:space="preserve">
<value>Period property '{entityType}.{propertyName}' can't have a default value specified.</value>
</data>
<data name="TemporalPeriodPropertyMustBeInShadowState" xml:space="preserve">
<value>Period property '{entityType}.{propertyName}' must be a shadow property.</value>
</data>
<data name="TemporalPeriodPropertyMustBeMappedToDatetime2" xml:space="preserve">
<value>Period property '{entityType}.{propertyName}' must be mapped to a column of type '{columnType}'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1093,25 +1093,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
skipBuild: true);

[ConditionalFact]
public async Task Temporal_table_works()
// Shadow properties. Issue #26007.
=> Assert.Equal(
SqlServerStrings.TemporalPeriodPropertyMustBeInShadowState("Customer", "PeriodStart"),
(await Assert.ThrowsAsync<InvalidOperationException>(() =>
TestAsync(
modelBuilder => modelBuilder.Entity(
"Customer", e =>
{
e.Property<int>("Id");
e.Property<string>("Name");
e.HasKey("Id");
e.ToTable(tb => tb.IsTemporal());
}),
new ModelCodeGenerationOptions { UseDataAnnotations = false },
code =>
{
AssertFileContents(
$$"""
public Task Temporal_table_works()
=> TestAsync(
modelBuilder => modelBuilder.Entity(
"Customer", e =>
{
e.Property<int>("Id");
e.Property<string>("Name");
e.HasKey("Id");
e.ToTable(tb => tb.IsTemporal());
}),
new ModelCodeGenerationOptions { UseDataAnnotations = false },
code =>
{
AssertFileContents(
$$"""
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -1157,12 +1153,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
""",
code.ContextFile);
},
model =>
{
// TODO
}))).Message);
code.ContextFile);
},
model =>
{
var entityType = model.FindEntityType("TestNamespace.Customer")!;
Assert.True(entityType.IsTemporal());
Assert.Equal("PeriodStart", entityType.GetPeriodStartPropertyName());
Assert.Equal("PeriodEnd", entityType.GetPeriodEndPropertyName());
});

[ConditionalFact]
public Task Sequences_work()
Expand Down
Loading
Loading