From 30d4b22651f497720d2a0b66852b45499d670ec1 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 11 Aug 2022 12:25:36 -0700 Subject: [PATCH] Add immutable annotation support to TableExpressionBase Resolves #28120 Only SelectExpression does mutable way, all other expressions will recreate with new annotations. --- ...yableMethodTranslatingExpressionVisitor.cs | 5 - ...yableMethodTranslatingExpressionVisitor.cs | 9 +- .../SqlExpressions/CrossApplyExpression.cs | 4 + .../SqlExpressions/CrossJoinExpression.cs | 4 + .../Query/SqlExpressions/ExceptExpression.cs | 4 + .../Query/SqlExpressions/FromSqlExpression.cs | 4 + .../SqlExpressions/InnerJoinExpression.cs | 4 + .../SqlExpressions/IntersectExpression.cs | 4 + .../SqlExpressions/LeftJoinExpression.cs | 4 + .../SqlExpressions/OuterApplyExpression.cs | 4 + .../SqlExpressions/SelectExpression.Helper.cs | 3 + .../Query/SqlExpressions/SelectExpression.cs | 46 +++++- .../Query/SqlExpressions/TableExpression.cs | 41 ++--- .../SqlExpressions/TableExpressionBase.cs | 149 +++--------------- .../TableValuedFunctionExpression.cs | 4 + .../Query/SqlExpressions/UnionExpression.cs | 4 + .../Internal/SqlServerQuerySqlGenerator.cs | 9 +- ...yableMethodTranslatingExpressionVisitor.cs | 24 +-- 18 files changed, 150 insertions(+), 176 deletions(-) diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs index 7f23ee47e03..1ca0441ec6a 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs @@ -1321,11 +1321,6 @@ entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression proj return innerShaper; } - - private static Expression AddConvertToObject(Expression expression) - => expression.Type.IsValueType - ? Expression.Convert(expression, typeof(object)) - : expression; } private ShapedQueryExpression TranslateTwoParameterSelector(ShapedQueryExpression source, LambdaExpression resultSelector) diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 4cb6e3af477..e41ba43fc6e 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -1655,11 +1655,11 @@ SelectExpression BuildInnerSelectExpressionForOwnedTypeMappedToDifferentTable( : foreignKey.PrincipalKey.Properties[0]); var sourceTable = FindRootTableExpressionForColumn(sourceColumn); - var ownedTable = new TableExpression(targetTable); + TableExpressionBase ownedTable = new TableExpression(targetTable); foreach (var annotation in sourceTable.GetAnnotations()) { - ownedTable.SetAnnotation(annotation.Name, annotation.Value); + ownedTable = ownedTable.AddAnnotation(annotation.Name, annotation.Value); } return _sqlExpressionFactory.Select(targetEntityType, ownedTable); @@ -1688,11 +1688,6 @@ static TableExpressionBase FindRootTableExpressionForColumn(ColumnExpression col } } - private static Expression AddConvertToObject(Expression expression) - => expression.Type.IsValueType - ? Expression.Convert(expression, typeof(object)) - : expression; - private EntityProjectionExpression GetEntityProjectionExpression(EntityShaperExpression entityShaperExpression) => entityShaperExpression.ValueBufferExpression switch { diff --git a/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs index 64c516c1b3f..9f58ec09b4b 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CrossApplyExpression.cs @@ -43,6 +43,10 @@ public virtual CrossApplyExpression Update(TableExpressionBase table) ? new CrossApplyExpression(table, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new CrossApplyExpression(Table, GetAnnotations()); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs index 84af081406a..c5e6c4eef09 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/CrossJoinExpression.cs @@ -43,6 +43,10 @@ public virtual CrossJoinExpression Update(TableExpressionBase table) ? new CrossJoinExpression(table, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new CrossJoinExpression(Table, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/ExceptExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/ExceptExpression.cs index 0b4a3c57909..dd834b5fd11 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/ExceptExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/ExceptExpression.cs @@ -61,6 +61,10 @@ public virtual ExceptExpression Update(SelectExpression source1, SelectExpressio ? new ExceptExpression(Alias, source1, source2, IsDistinct, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new ExceptExpression(Alias, Source1, Source2, IsDistinct, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/FromSqlExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/FromSqlExpression.cs index 344ff9bb7f8..f378a6408a8 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/FromSqlExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/FromSqlExpression.cs @@ -83,6 +83,10 @@ public virtual FromSqlExpression Update(Expression arguments) ? new FromSqlExpression(Alias, _table, Sql, arguments, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new FromSqlExpression(Alias, _table, Sql, Arguments, annotations); + /// ITableBase ITableBasedExpression.Table => _table; diff --git a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs index dc7e521e9b0..dc4fc43fa47 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/InnerJoinExpression.cs @@ -53,6 +53,10 @@ public virtual InnerJoinExpression Update(TableExpressionBase table, SqlExpressi ? new InnerJoinExpression(table, joinPredicate, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new InnerJoinExpression(Table, JoinPredicate, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/IntersectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/IntersectExpression.cs index 87b2ff22af9..b836e278043 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/IntersectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/IntersectExpression.cs @@ -61,6 +61,10 @@ public virtual IntersectExpression Update(SelectExpression source1, SelectExpres ? new IntersectExpression(Alias, source1, source2, IsDistinct, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new IntersectExpression(Alias, Source1, Source2, IsDistinct, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs index 32e5c6145ac..ca8e42132ec 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/LeftJoinExpression.cs @@ -53,6 +53,10 @@ public virtual LeftJoinExpression Update(TableExpressionBase table, SqlExpressio ? new LeftJoinExpression(table, joinPredicate, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new LeftJoinExpression(Table, JoinPredicate, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs index 84c66157b6f..2cc30f8dda0 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/OuterApplyExpression.cs @@ -43,6 +43,10 @@ public virtual OuterApplyExpression Update(TableExpressionBase table) ? new OuterApplyExpression(table, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new OuterApplyExpression(Table, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs index c85a41aa11a..57624e48133 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs @@ -513,6 +513,9 @@ public TpcTablesExpression Prune(IReadOnlyList discriminatorValues) // This is implementation detail hence visitors are not supposed to see inside unless they really need to. protected override Expression VisitChildren(ExpressionVisitor visitor) => this; + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new TpcTablesExpression(Alias, EntityType, SelectExpressions, annotations); + protected override void Print(ExpressionPrinter expressionPrinter) { expressionPrinter.AppendLine("("); diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 8ed2e6ad81a..f2d0a8617f3 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -56,6 +56,8 @@ public sealed partial class SelectExpression : TableExpressionBase private readonly List _aliasForClientProjections = new(); private CloningExpressionVisitor? _cloningExpressionVisitor; + private SortedDictionary? _annotations; + #if DEBUG private List? _removedAliases; #endif @@ -68,13 +70,22 @@ private SelectExpression( List groupBy, List orderings, IEnumerable annotations) - : base(alias, annotations) + : base(alias) { _projection = projections; _tables = tables; _tableReferences = tableReferences; _groupBy = groupBy; _orderings = orderings; + + if (annotations != null) + { + _annotations = new SortedDictionary(); + foreach (var annotation in annotations) + { + _annotations[annotation.Name] = annotation; + } + } } private SelectExpression(string? alias) @@ -3776,6 +3787,39 @@ public SelectExpression Update( return newSelectExpression; } + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => throw new NotImplementedException("inconceivable"); + + /// + public override TableExpressionBase AddAnnotation(string name, object? value) + { + var oldAnnotation = FindAnnotation(name); + if (oldAnnotation != null) + { + return Equals(oldAnnotation.Value, value) + ? this + : throw new InvalidOperationException(CoreStrings.DuplicateAnnotation(name, this.Print())); + } + + _annotations ??= new(); + _annotations[name] = new Annotation(name, value); + + return this; + } + + /// + public override IAnnotation? FindAnnotation(string name) + => _annotations == null + ? null + : _annotations.TryGetValue(name, out var annotation) + ? annotation + : null; + + /// + public override IEnumerable GetAnnotations() + => _annotations?.Values ?? Enumerable.Empty(); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/TableExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/TableExpression.cs index 959cfdd2f34..3f4c0a62d7b 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/TableExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/TableExpression.cs @@ -28,23 +28,6 @@ private TableExpression(ITableBase table, IEnumerable? annotations) Table = table; } - /// - ITableBase ITableBasedExpression.Table => Table; - - /// - protected override void Print(ExpressionPrinter expressionPrinter) - { - if (!string.IsNullOrEmpty(Schema)) - { - expressionPrinter.Append(Schema).Append("."); - } - - expressionPrinter.Append(Name); - PrintAnnotations(expressionPrinter); - - expressionPrinter.Append(" AS ").Append(Alias); - } - /// /// The alias assigned to this table source. /// @@ -71,8 +54,28 @@ public override string? Alias public ITableBase Table { get; } /// - public TableExpressionBase Clone() - => new TableExpression(Table, GetAnnotations()) { Alias = Alias }; + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new TableExpression(Table, annotations) { Alias = Alias }; + + /// + ITableBase ITableBasedExpression.Table => Table; + + /// + protected override void Print(ExpressionPrinter expressionPrinter) + { + if (!string.IsNullOrEmpty(Schema)) + { + expressionPrinter.Append(Schema).Append("."); + } + + expressionPrinter.Append(Name); + PrintAnnotations(expressionPrinter); + + expressionPrinter.Append(" AS ").Append(Alias); + } + + /// + public TableExpressionBase Clone() => CreateWithAnnotations(GetAnnotations()); /// public override bool Equals(object? obj) diff --git a/src/EFCore.Relational/Query/SqlExpressions/TableExpressionBase.cs b/src/EFCore.Relational/Query/SqlExpressions/TableExpressionBase.cs index e4cfb2342c8..d535dc2a3ea 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/TableExpressionBase.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/TableExpressionBase.cs @@ -12,9 +12,9 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions; /// not used in application code. /// /// -public abstract class TableExpressionBase : Expression, IPrintableExpression, IMutableAnnotatable +public abstract class TableExpressionBase : Expression, IPrintableExpression { - private SortedDictionary? _annotations; + private readonly IReadOnlyDictionary? _annotations; /// /// Creates a new instance of the class. @@ -37,10 +37,13 @@ protected TableExpressionBase(string? alias, IEnumerable? annotatio if (annotations != null) { + var dictionary = new SortedDictionary(); foreach (var annotation in annotations) { - SetAnnotation(annotation.Name, annotation.Value); + dictionary[annotation.Name] = annotation; } + + _annotations = dictionary; } } @@ -61,33 +64,6 @@ public override Type Type public sealed override ExpressionType NodeType => ExpressionType.Extension; - /// - /// Gets the value annotation with the given name, returning if it does not exist. - /// - /// The key of the annotation to find. - /// - /// The value of the existing annotation if an annotation with the specified name already exists. - /// Otherwise, . - /// - public virtual object? this[string name] - { - get => FindAnnotation(name)?.Value; - - set - { - Check.NotEmpty(name, nameof(name)); - - if (value == null) - { - RemoveAnnotation(name); - } - else - { - SetAnnotation(name, value); - } - } - } - /// /// Creates a printable string representation of the given expression using . /// @@ -132,87 +108,28 @@ public override int GetHashCode() /// /// The key of the annotation to be added. /// The value to be stored in the annotation. - /// The newly added annotation. - public virtual Annotation AddAnnotation(string name, object? value) - => AddAnnotation(name, new(name, value)); - - /// - /// Adds an annotation to this object. Throws if an annotation with the specified name already exists. - /// - /// The key of the annotation to be added. - /// The annotation to be added. - /// The added annotation. - protected virtual Annotation AddAnnotation(string name, Annotation annotation) - { - if (FindAnnotation(name) != null) - { - throw new InvalidOperationException(CoreStrings.DuplicateAnnotation(name, ToString())); - } - - SetAnnotation(name, annotation, oldAnnotation: null); - - return annotation; - } - - /// - /// Sets the annotation stored under the given key. Overwrites the existing annotation if an - /// annotation with the specified name already exists. - /// - /// The key of the annotation to be added. - /// The value to be stored in the annotation. - public virtual void SetAnnotation(string name, object? value) + /// The new expression with annotation applied to it. + public virtual TableExpressionBase AddAnnotation(string name, object? value) { var oldAnnotation = FindAnnotation(name); - if (oldAnnotation != null - && Equals(oldAnnotation.Value, value)) + if (oldAnnotation != null) { - return; + return Equals(oldAnnotation.Value, value) + ? this + : throw new InvalidOperationException(CoreStrings.DuplicateAnnotation(name, this.Print())); } - SetAnnotation(name, new(name, value), oldAnnotation); - } + var annotation = new Annotation(name, value); - /// - /// Sets the annotation stored under the given key. Overwrites the existing annotation if an - /// annotation with the specified name already exists. - /// - /// The key of the annotation to be added. - /// The annotation to be set. - /// The annotation being replaced. - /// The annotation that was set. - protected virtual Annotation? SetAnnotation( - string name, - Annotation annotation, - Annotation? oldAnnotation) - { - _annotations ??= new SortedDictionary(StringComparer.Ordinal); - _annotations[name] = annotation; - - return annotation; + return CreateWithAnnotations(new[] { annotation }.Concat(GetAnnotations())); } /// - /// Removes the given annotation from this object. + /// Creates an object like this with specified annotations. /// - /// The annotation to remove. - /// The annotation that was removed. - public virtual Annotation? RemoveAnnotation(string name) - { - var annotation = FindAnnotation(name); - if (annotation == null) - { - return null; - } - - _annotations!.Remove(name); - - if (_annotations.Count == 0) - { - _annotations = null; - } - - return annotation; - } + /// The annotations to be applied. + /// The new expression with given annotations. + protected abstract TableExpressionBase CreateWithAnnotations(IEnumerable annotations); /// /// Gets the annotation with the given name, returning if it does not exist. @@ -221,40 +138,16 @@ public virtual void SetAnnotation(string name, object? value) /// /// The existing annotation if an annotation with the specified name already exists. Otherwise, . /// - public virtual Annotation? FindAnnotation(string name) - { - Check.NotEmpty(name, nameof(name)); - - return _annotations == null + public virtual IAnnotation? FindAnnotation(string name) + => _annotations == null ? null : _annotations.TryGetValue(name, out var annotation) ? annotation : null; - } /// /// Gets all annotations on the current object. /// public virtual IEnumerable GetAnnotations() - => _annotations?.Values ?? Enumerable.Empty(); - - /// - [DebuggerStepThrough] - IAnnotation IMutableAnnotatable.AddAnnotation(string name, object? value) - => AddAnnotation(name, value); - - /// - [DebuggerStepThrough] - IAnnotation? IMutableAnnotatable.RemoveAnnotation(string name) - => RemoveAnnotation(name); - - /// - [DebuggerStepThrough] - void IMutableAnnotatable.SetOrRemoveAnnotation(string name, object? value) - => this[name] = value; - - /// - [DebuggerStepThrough] - IAnnotation? IReadOnlyAnnotatable.FindAnnotation(string name) - => FindAnnotation(name); + => _annotations?.Values ?? Enumerable.Empty(); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/TableValuedFunctionExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/TableValuedFunctionExpression.cs index db40d23cd9d..5c556f64c1b 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/TableValuedFunctionExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/TableValuedFunctionExpression.cs @@ -91,6 +91,10 @@ public virtual TableValuedFunctionExpression Update(IReadOnlyList ? new TableValuedFunctionExpression(Alias, StoreFunction, arguments, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new TableValuedFunctionExpression(Alias, StoreFunction, Arguments, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.Relational/Query/SqlExpressions/UnionExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/UnionExpression.cs index 617b53ee418..f72196243f2 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/UnionExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/UnionExpression.cs @@ -61,6 +61,10 @@ public virtual UnionExpression Update(SelectExpression source1, SelectExpression ? new UnionExpression(Alias, source1, source2, IsDistinct, GetAnnotations()) : this; + /// + protected override TableExpressionBase CreateWithAnnotations(IEnumerable annotations) + => new UnionExpression(Alias, Source1, Source2, IsDistinct, annotations); + /// protected override void Print(ExpressionPrinter expressionPrinter) { diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index 85d4bd799dc..85d8489f975 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -227,7 +227,8 @@ when tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalOperationTy Sql.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(tableExpression.Name, tableExpression.Schema)) .Append(" FOR SYSTEM_TIME "); - var temporalOperationType = (TemporalOperationType)tableExpression[SqlServerAnnotationNames.TemporalOperationType]!; + var temporalOperationType = (TemporalOperationType)tableExpression + .FindAnnotation(SqlServerAnnotationNames.TemporalOperationType)!.Value!; switch (temporalOperationType) { @@ -236,7 +237,7 @@ when tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalOperationTy break; case TemporalOperationType.AsOf: - var pointInTime = (DateTime)tableExpression[SqlServerAnnotationNames.TemporalAsOfPointInTime]!; + var pointInTime = (DateTime)tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalAsOfPointInTime)!.Value!; Sql.Append("AS OF ") .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(pointInTime)); @@ -246,10 +247,10 @@ when tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalOperationTy case TemporalOperationType.ContainedIn: case TemporalOperationType.FromTo: var from = _typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral( - (DateTime)tableExpression[SqlServerAnnotationNames.TemporalRangeOperationFrom]!); + (DateTime)tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalRangeOperationFrom)!.Value!); var to = _typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral( - (DateTime)tableExpression[SqlServerAnnotationNames.TemporalRangeOperationTo]!); + (DateTime)tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalRangeOperationTo)!.Value!); switch (temporalOperationType) { diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index 66319ea677c..8c9b9bacc02 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -69,30 +69,30 @@ protected override Expression VisitExtension(Expression extensionExpression) switch (queryRootExpression) { case TemporalAllQueryRootExpression: - tableExpression[SqlServerAnnotationNames.TemporalOperationType] = TemporalOperationType.All; + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalOperationType, TemporalOperationType.All); break; case TemporalAsOfQueryRootExpression asOf: - tableExpression[SqlServerAnnotationNames.TemporalOperationType] = TemporalOperationType.AsOf; - tableExpression[SqlServerAnnotationNames.TemporalAsOfPointInTime] = asOf.PointInTime; + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalOperationType, TemporalOperationType.AsOf); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalAsOfPointInTime, asOf.PointInTime); break; case TemporalBetweenQueryRootExpression between: - tableExpression[SqlServerAnnotationNames.TemporalOperationType] = TemporalOperationType.Between; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationFrom] = between.From; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationTo] = between.To; + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalOperationType, TemporalOperationType.Between); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationFrom, between.From); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationTo, between.To); break; case TemporalContainedInQueryRootExpression containedIn: - tableExpression[SqlServerAnnotationNames.TemporalOperationType] = TemporalOperationType.ContainedIn; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationFrom] = containedIn.From; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationTo] = containedIn.To; + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalOperationType, TemporalOperationType.ContainedIn); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationFrom, containedIn.From); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationTo, containedIn.To); break; case TemporalFromToQueryRootExpression fromTo: - tableExpression[SqlServerAnnotationNames.TemporalOperationType] = TemporalOperationType.FromTo; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationFrom] = fromTo.From; - tableExpression[SqlServerAnnotationNames.TemporalRangeOperationTo] = fromTo.To; + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalOperationType, TemporalOperationType.FromTo); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationFrom, fromTo.From); + tableExpression.AddAnnotation(SqlServerAnnotationNames.TemporalRangeOperationTo, fromTo.To); break; default: