From ad033a2633db0538f7fcae85cfc59b353ee51727 Mon Sep 17 00:00:00 2001 From: masesdevelopers <94312179+masesdevelopers@users.noreply.github.com> Date: Thu, 7 Dec 2023 17:21:59 +0100 Subject: [PATCH 1/3] First update of Kafka Stream engine --- .../Storage/Internal/EntityTypeProducer.cs | 22 ++- .../KEFCore/Storage/Internal/KafkaRowBag.cs | 23 ++-- .../Internal/KafkaStreamsBaseRetriever.cs | 126 +++++++++++------- .../Internal/KafkaStreamsTableRetriever.cs | 15 +-- .../Internal/KafkaValueGeneratorSelector.cs | 10 +- 5 files changed, 105 insertions(+), 91 deletions(-) diff --git a/src/net/KEFCore/Storage/Internal/EntityTypeProducer.cs b/src/net/KEFCore/Storage/Internal/EntityTypeProducer.cs index 099ad82f..17f6d29f 100644 --- a/src/net/KEFCore/Storage/Internal/EntityTypeProducer.cs +++ b/src/net/KEFCore/Storage/Internal/EntityTypeProducer.cs @@ -53,10 +53,10 @@ public class EntityTypeProducer? _onChangeEvent; #region KNetCompactedReplicatorEnumerable - class KNetCompactedReplicatorEnumerable : IEnumerable + class KNetCompactedReplicatorEnumerable(IEntityType entityType, IKNetCompactedReplicator? kafkaCompactedReplicator) : IEnumerable { - readonly IEntityType _entityType; - readonly IKNetCompactedReplicator? _kafkaCompactedReplicator; + readonly IEntityType _entityType = entityType; + readonly IKNetCompactedReplicator? _kafkaCompactedReplicator = kafkaCompactedReplicator; #region KNetCompactedReplicatorEnumerator class KNetCompactedReplicatorEnumerator : IEnumerator @@ -130,8 +130,8 @@ public bool MoveNext() if (_enumerator != null && _enumerator.MoveNext()) { #if DEBUG_PERFORMANCE - _cycles++; - _valueBufferSw.Start(); + _cycles++; + _valueBufferSw.Start(); #endif object[] array = null!; _enumerator.Current.Value.GetData(_entityType, ref array); @@ -161,13 +161,8 @@ public void Reset() _enumerator?.Reset(); } } - #endregion - public KNetCompactedReplicatorEnumerable(IEntityType entityType, IKNetCompactedReplicator? kafkaCompactedReplicator) - { - _entityType = entityType; - _kafkaCompactedReplicator = kafkaCompactedReplicator; - } + #endregion public IEnumerator GetEnumerator() { @@ -247,7 +242,7 @@ public IEnumerable> Commit(IEnumerable reco { if (_useCompactedReplicator) { - foreach (KafkaRowBag record in records) + foreach (KafkaRowBag record in records.Cast>()) { var value = record.Value(TValueContainerConstructor); if (_kafkaCompactedReplicator != null) _kafkaCompactedReplicator[record.Key] = value!; @@ -258,7 +253,7 @@ public IEnumerable> Commit(IEnumerable reco else { List> futures = new(); - foreach (KafkaRowBag record in records) + foreach (KafkaRowBag record in records.Cast>()) { var future = _kafkaProducer?.Send(new KNetProducerRecord(record.AssociatedTopicName, 0, record.Key, record.Value(TValueContainerConstructor)!)); futures.Add(future!); @@ -276,6 +271,7 @@ public void Dispose() { if (_onChangeEvent != null) { + _kafkaCompactedReplicator.OnRemoteAdd -= KafkaCompactedReplicator_OnRemoteAdd; _kafkaCompactedReplicator.OnRemoteUpdate -= KafkaCompactedReplicator_OnRemoteUpdate; _kafkaCompactedReplicator.OnRemoteRemove -= KafkaCompactedReplicator_OnRemoteRemove; } diff --git a/src/net/KEFCore/Storage/Internal/KafkaRowBag.cs b/src/net/KEFCore/Storage/Internal/KafkaRowBag.cs index c7f54c8b..42879209 100644 --- a/src/net/KEFCore/Storage/Internal/KafkaRowBag.cs +++ b/src/net/KEFCore/Storage/Internal/KafkaRowBag.cs @@ -27,28 +27,21 @@ namespace MASES.EntityFrameworkCore.KNet.Storage.Internal; /// 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. /// -public class KafkaRowBag : IKafkaRowBag +/// +/// Default initializer +/// +public class KafkaRowBag(IUpdateEntry entry, string topicName, TKey key, object?[]? row) : IKafkaRowBag where TKey : notnull where TValueContainer : IValueContainer { - /// - /// Default initializer - /// - public KafkaRowBag(IUpdateEntry entry, string topicName, TKey key, object?[]? row) - { - UpdateEntry = entry; - AssociatedTopicName = topicName; - Key = key; - ValueBuffer = row; - } /// - public IUpdateEntry UpdateEntry { get; private set; } + public IUpdateEntry UpdateEntry { get; } = entry; /// - public string AssociatedTopicName { get; private set; } + public string AssociatedTopicName { get; } = topicName; /// /// The Key /// - public TKey Key { get; private set; } + public TKey Key { get; } = key; /// /// The Value /// @@ -56,5 +49,5 @@ public KafkaRowBag(IUpdateEntry entry, string topicName, TKey key, object?[]? ro /// /// The content /// - public object?[]? ValueBuffer { get; private set; } + public object?[]? ValueBuffer { get; } = row; } diff --git a/src/net/KEFCore/Storage/Internal/KafkaStreamsBaseRetriever.cs b/src/net/KEFCore/Storage/Internal/KafkaStreamsBaseRetriever.cs index d6f446f5..78147d32 100644 --- a/src/net/KEFCore/Storage/Internal/KafkaStreamsBaseRetriever.cs +++ b/src/net/KEFCore/Storage/Internal/KafkaStreamsBaseRetriever.cs @@ -55,7 +55,9 @@ public class KafkaStreamsBaseRetriever : IKafkaStreamsBaseRe private readonly IKNetSerDes _keySerdes; private readonly IKNetSerDes _valueSerdes; private readonly StreamsBuilder _builder; - private readonly KStream _root; + private readonly GlobalKTable _globalTable; + private readonly KeyValueBytesStoreSupplier _storeSupplier; + private readonly Materialized> _materialized; private readonly AutoResetEvent _dataReceived = new(false); private readonly AutoResetEvent _resetEvent = new(false); @@ -66,6 +68,8 @@ public class KafkaStreamsBaseRetriever : IKafkaStreamsBaseRe private StreamsUncaughtExceptionHandler? _errorHandler; private KafkaStreams.StateListener? _stateListener; + private readonly bool _usePersistentStorage; + private readonly string _topicName; private readonly string _storageId; private Exception? _resultException = null; private KafkaStreams.State _currentState = KafkaStreams.State.NOT_RUNNING; @@ -73,26 +77,28 @@ public class KafkaStreamsBaseRetriever : IKafkaStreamsBaseRe /// /// Default initializer /// - public KafkaStreamsBaseRetriever(IKafkaCluster kafkaCluster, IEntityType entityType, IKNetSerDes keySerdes, IKNetSerDes valueSerdes, string storageId, StreamsBuilder builder, KStream root) + public KafkaStreamsBaseRetriever(IKafkaCluster kafkaCluster, IEntityType entityType, IKNetSerDes keySerdes, IKNetSerDes valueSerdes, StreamsBuilder builder) { _kafkaCluster = kafkaCluster; _entityType = entityType; _keySerdes = keySerdes; _valueSerdes = valueSerdes; + _topicName = _entityType.TopicName(kafkaCluster.Options); + _usePersistentStorage = _kafkaCluster.Options.UsePersistentStorage; + string storageId = entityType.StorageIdForTable(_kafkaCluster.Options); + _storageId = _usePersistentStorage ? storageId : Process.GetCurrentProcess().ProcessName + "-" + storageId; + _builder = builder; - _root = root; - _storageId = _kafkaCluster.Options.UsePersistentStorage ? storageId : Process.GetCurrentProcess().ProcessName + "-" + storageId; + _storeSupplier = _usePersistentStorage ? Stores.PersistentKeyValueStore(_storageId) : Stores.InMemoryKeyValueStore(_storageId); + _materialized = Materialized>.As(_storeSupplier); + _globalTable = _builder.GlobalTable(_entityType.TopicName(_kafkaCluster.Options), _materialized); - StartTopology(_builder, _root); + StartTopology(); } - private void StartTopology(StreamsBuilder builder, KStream root) + private void StartTopology() { - var storeSupplier = _kafkaCluster.Options.UsePersistentStorage ? Stores.PersistentKeyValueStore(_storageId) : Stores.InMemoryKeyValueStore(_storageId); - var materialized = Materialized>.As(storeSupplier); - root.ToTable(materialized); - - _streams = new(builder.Build(), _kafkaCluster.Options.StreamsOptions(_entityType)); + _streams = new(_builder.Build(), _kafkaCluster.Options.StreamsOptions(_entityType)); _errorHandler = new() { @@ -110,7 +116,7 @@ private void StartTopology(StreamsBuilder builder, KStream root) { _currentState = newState; #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"StateListener of {_entityType.Name} oldState: {oldState} newState: {newState} on {DateTime.Now:HH:mm:ss.FFFFFFF}"); + Infrastructure.KafkaDbContext.ReportString($"StateListener of {_entityType.Name} oldState: {oldState} newState: {newState} on {DateTime.Now:HH:mm:ss.FFFFFFF}"); #endif if (_stateChanged != null && !_stateChanged.SafeWaitHandle.IsClosed) _stateChanged.Set(); } @@ -126,18 +132,18 @@ private void StartTopology(StreamsBuilder builder, KStream root) try { _resetEvent.Set(); - var index = WaitHandle.WaitAny(new WaitHandle[] { _stateChanged, _exceptionSet }); + var index = WaitHandle.WaitAny([_stateChanged, _exceptionSet]); if (index == 1) return; while (true) { - index = WaitHandle.WaitAny(new WaitHandle[] { _stateChanged, _dataReceived, _exceptionSet }, waitingTime); + index = WaitHandle.WaitAny([_stateChanged, _dataReceived, _exceptionSet], waitingTime); if (index == 2) return; if (_currentState.Equals(KafkaStreams.State.CREATED) || _currentState.Equals(KafkaStreams.State.REBALANCING)) { if (index == WaitHandle.WaitTimeout) { #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"State of {_entityType.Name}: {_currentState} No handle set within {waitingTime} ms"); + Infrastructure.KafkaDbContext.ReportString($"State of {_entityType.Name}: {_currentState} No handle set within {waitingTime} ms"); #endif continue; } @@ -160,7 +166,7 @@ private void StartTopology(StreamsBuilder builder, KStream root) _resetEvent.WaitOne(); _streams.Start(); #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"KafkaStreamsBaseRetriever on {_entityType.Name} started on {DateTime.Now:HH:mm:ss.FFFFFFF}"); + Infrastructure.KafkaDbContext.ReportString($"KafkaStreamsBaseRetriever on {_entityType.Name} started on {DateTime.Now:HH:mm:ss.FFFFFFF}"); #endif _resetEvent.WaitOne(); // wait running state if (_resultException != null) throw _resultException; @@ -172,7 +178,7 @@ public IEnumerator GetEnumerator() { if (_resultException != null) throw _resultException; #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"Requested KafkaEnumerator for {_entityType.Name} on {DateTime.Now:HH:mm:ss.FFFFFFF}"); + Infrastructure.KafkaDbContext.ReportString($"Requested KafkaEnumerator for {_entityType.Name} on {DateTime.Now:HH:mm:ss.FFFFFFF}"); #endif return new KafkaEnumerator(_kafkaCluster, _entityType, _keySerdes, _valueSerdes, keyValueStore); } @@ -208,10 +214,10 @@ class KafkaEnumerator : IEnumerator private IEnumerator>? keyValueEnumerator = null; #if DEBUG_PERFORMANCE - Stopwatch _moveNextSw = new Stopwatch(); - Stopwatch _currentSw = new Stopwatch(); - Stopwatch _valueSerdesSw = new Stopwatch(); - Stopwatch _valueBufferSw = new Stopwatch(); + Stopwatch _moveNextSw = new Stopwatch(); + Stopwatch _currentSw = new Stopwatch(); + Stopwatch _valueSerdesSw = new Stopwatch(); + Stopwatch _valueBufferSw = new Stopwatch(); #endif public KafkaEnumerator(IKafkaCluster kafkaCluster, IEntityType entityType, IKNetSerDes keySerdes, IKNetSerDes valueSerdes, ReadOnlyKeyValueStore? keyValueStore) @@ -223,12 +229,14 @@ public KafkaEnumerator(IKafkaCluster kafkaCluster, IEntityType entityType, IKNet _valueSerdes = valueSerdes ?? throw new ArgumentNullException(nameof(valueSerdes)); _keyValueStore = keyValueStore; #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"KafkaEnumerator for {_entityType.Name} - ApproximateNumEntries {_keyValueStore?.ApproximateNumEntries()}"); + Infrastructure.KafkaDbContext.ReportString($"KafkaEnumerator for {_entityType.Name} - ApproximateNumEntries {_keyValueStore?.ApproximateNumEntries()}"); #endif keyValueIterator = _keyValueStore?.All(); keyValueEnumerator = keyValueIterator?.ToIEnumerator(); } + ValueBuffer? _current = null; + public ValueBuffer Current { get @@ -238,31 +246,7 @@ public ValueBuffer Current { _currentSw.Start(); #endif - if (keyValueEnumerator != null) - { - var kv = keyValueEnumerator.Current; - object? v = kv.value; -#if DEBUG_PERFORMANCE - _valueSerdesSw.Start(); -#endif - TValue entityTypeData = _valueSerdes.DeserializeWithHeaders(null, null, v as byte[]); -#if DEBUG_PERFORMANCE - _valueSerdesSw.Stop(); - _valueBufferSw.Start(); -#endif - object[] array = null!; - entityTypeData.GetData(_entityType, ref array); -#if DEBUG_PERFORMANCE - _valueBufferSw.Stop(); -#endif - var data = new ValueBuffer(array); - if (data.IsEmpty) - { - throw new InvalidOperationException("Data is Empty"); - } - return data; - } - throw new InvalidOperationException("InvalidEnumerator"); + return _current ?? default; #if DEBUG_PERFORMANCE } finally @@ -278,11 +262,15 @@ public ValueBuffer Current public void Dispose() { #if DEBUG_PERFORMANCE - Infrastructure.KafkaDbContext.ReportString($"KafkaEnumerator _moveNextSw: {_moveNextSw.Elapsed} _currentSw: {_currentSw.Elapsed} _valueSerdesSw: {_valueSerdesSw.Elapsed} _valueBufferSw: {_valueBufferSw.Elapsed}"); + Infrastructure.KafkaDbContext.ReportString($"KafkaEnumerator _moveNextSw: {_moveNextSw.Elapsed} _currentSw: {_currentSw.Elapsed} _valueSerdesSw: {_valueSerdesSw.Elapsed} _valueBufferSw: {_valueBufferSw.Elapsed}"); #endif keyValueIterator?.Dispose(); } +#if DEBUG_PERFORMANCE + int _cycles = 0; +#endif + public bool MoveNext() { #if DEBUG_PERFORMANCE @@ -290,10 +278,48 @@ public bool MoveNext() { _moveNextSw.Start(); #endif - return (keyValueEnumerator != null) && keyValueEnumerator.MoveNext(); + if (keyValueEnumerator != null && keyValueEnumerator.MoveNext()) + { #if DEBUG_PERFORMANCE + _cycles++; + _valueBufferSw.Start(); +#endif + bool startedGCRegion = GC.TryStartNoGCRegion(1024 * 1024); + byte[]? data; + using (KeyValue kv = keyValueEnumerator.Current) + { + data = kv.value as byte[]; + } + if (startedGCRegion) GC.EndNoGCRegion(); +#if DEBUG_PERFORMANCE + _valueSerdesSw.Start(); +#endif + TValue entityTypeData = _valueSerdes.DeserializeWithHeaders(null, null, data); +#if DEBUG_PERFORMANCE + _valueSerdesSw.Stop(); + _valueBufferSw.Start(); +#endif + object[] array = null!; + entityTypeData.GetData(_entityType, ref array); +#if DEBUG_PERFORMANCE + _valueBufferSw.Stop(); +#endif + _current = new ValueBuffer(array); + + return true; + } + _current = null; + return false; +#if DEBUG_PERFORMANCE + } + finally + { + _moveNextSw.Stop(); + if (_cycles == 0) + { + throw new InvalidOperationException($"KafkaEnumerator - No data returned from {keyValueEnumerator}"); + } } - finally { _moveNextSw.Stop(); } #endif } diff --git a/src/net/KEFCore/Storage/Internal/KafkaStreamsTableRetriever.cs b/src/net/KEFCore/Storage/Internal/KafkaStreamsTableRetriever.cs index a24714d5..774604da 100644 --- a/src/net/KEFCore/Storage/Internal/KafkaStreamsTableRetriever.cs +++ b/src/net/KEFCore/Storage/Internal/KafkaStreamsTableRetriever.cs @@ -30,10 +30,16 @@ namespace MASES.EntityFrameworkCore.KNet.Storage.Internal; /// 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. /// -public sealed class KafkaStreamsTableRetriever : KafkaStreamsBaseRetriever +/// +/// Default initializer +/// +public sealed class KafkaStreamsTableRetriever(IKafkaCluster kafkaCluster, IEntityType entityType, IKNetSerDes keySerdes, IKNetSerDes valueSerdes, StreamsBuilder builder) + : KafkaStreamsBaseRetriever(kafkaCluster, entityType, keySerdes, valueSerdes, builder) where TKey :notnull where TValueContainer : IValueContainer { + static readonly StreamsBuilder GlobalBuilder = new(); + /// /// Initializer /// @@ -41,12 +47,5 @@ public KafkaStreamsTableRetriever(IKafkaCluster kafkaCluster, IEntityType entity : this(kafkaCluster, entityType, keySerdes, valueSerdes, new StreamsBuilder()) { } - /// - /// Initializer - /// - public KafkaStreamsTableRetriever(IKafkaCluster kafkaCluster, IEntityType entityType, IKNetSerDes keySerdes, IKNetSerDes valueSerdes, StreamsBuilder builder) - : base(kafkaCluster, entityType, keySerdes, valueSerdes, entityType.StorageIdForTable(kafkaCluster.Options), builder, builder.Stream(entityType.TopicName(kafkaCluster.Options))) - { - } } diff --git a/src/net/KEFCore/ValueGeneration/Internal/KafkaValueGeneratorSelector.cs b/src/net/KEFCore/ValueGeneration/Internal/KafkaValueGeneratorSelector.cs index 0465b08a..4daf201d 100644 --- a/src/net/KEFCore/ValueGeneration/Internal/KafkaValueGeneratorSelector.cs +++ b/src/net/KEFCore/ValueGeneration/Internal/KafkaValueGeneratorSelector.cs @@ -49,11 +49,11 @@ public override ValueGenerator Select(IProperty property, IEntityType entityType #else /// public override ValueGenerator Select(IProperty property, ITypeBase typeBase) - => property.GetValueGeneratorFactory() == null - && property.ClrType.IsInteger() - && property.ClrType.UnwrapNullableType() != typeof(char) - ? GetOrCreate(property) - : base.Select(property, typeBase); + => property.GetValueGeneratorFactory() == null + && property.ClrType.IsInteger() + && property.ClrType.UnwrapNullableType() != typeof(char) + ? GetOrCreate(property) + : base.Select(property, typeBase); #endif private ValueGenerator GetOrCreate(IProperty property) { From a1de60124d26176cb1924c0dc27734d0fc7cdd2e Mon Sep 17 00:00:00 2001 From: masesdevelopers <94312179+masesdevelopers@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:02:30 +0100 Subject: [PATCH 2/3] Merge Shared8 into Shared --- src/net/KEFCore/KEFCore.csproj | 18 +- src/net/KEFCore/Shared/Check.cs | 19 +- .../KEFCore/Shared/DictionaryExtensions.cs | 17 +- .../KEFCore/Shared/DisposableExtensions.cs | 3 - .../KEFCore/Shared/EnumerableExtensions.cs | 10 +- src/net/KEFCore/Shared/EnumerableMethods.cs | 44 +- .../KEFCore/Shared/ExpressionExtensions.cs | 29 +- .../ExpressionVisitorExtensions.cs | 0 src/net/KEFCore/Shared/Graph.cs | 5 - .../{Shared8 => Shared}/HashHelpers.cs | 0 .../IDictionaryDebugView.cs | 0 .../KEFCore/Shared/MemberInfoExtensions.cs | 7 +- .../KEFCore/Shared/MethodInfoExtensions.cs | 7 +- src/net/KEFCore/Shared/Multigraph.cs | 331 +++++----- .../Shared/NonCapturingLazyInitializer.cs | 6 +- .../OrderedDictionary.KeyCollection.cs | 0 .../OrderedDictionary.ValueCollection.cs | 0 .../{Shared8 => Shared}/OrderedDictionary.cs | 0 .../KEFCore/Shared/PropertyInfoExtensions.cs | 13 +- .../{Shared8 => Shared}/SharedStopwatch.cs | 0 .../KEFCore/Shared/SharedTypeExtensions.cs | 89 ++- .../KEFCore/Shared/StringBuilderExtensions.cs | 3 - src/net/KEFCore/Shared8/Check.cs | 123 ---- src/net/KEFCore/Shared8/CodeAnnotations.cs | 97 --- .../KEFCore/Shared8/DictionaryExtensions.cs | 101 --- .../KEFCore/Shared8/DisposableExtensions.cs | 24 - .../KEFCore/Shared8/EnumerableExtensions.cs | 145 ---- src/net/KEFCore/Shared8/EnumerableMethods.cs | 615 ----------------- .../KEFCore/Shared8/ExpressionExtensions.cs | 51 -- src/net/KEFCore/Shared8/Graph.cs | 39 -- .../KEFCore/Shared8/MemberInfoExtensions.cs | 50 -- .../KEFCore/Shared8/MethodInfoExtensions.cs | 20 - src/net/KEFCore/Shared8/Multigraph.cs | 386 ----------- .../Shared8/NonCapturingLazyInitializer.cs | 130 ---- .../KEFCore/Shared8/PropertyInfoExtensions.cs | 43 -- .../KEFCore/Shared8/SharedTypeExtensions.cs | 619 ------------------ .../Shared8/StringBuilderExtensions.cs | 111 ---- 37 files changed, 301 insertions(+), 2854 deletions(-) rename src/net/KEFCore/{Shared8 => Shared}/ExpressionVisitorExtensions.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/HashHelpers.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/IDictionaryDebugView.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/OrderedDictionary.KeyCollection.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/OrderedDictionary.ValueCollection.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/OrderedDictionary.cs (100%) rename src/net/KEFCore/{Shared8 => Shared}/SharedStopwatch.cs (100%) delete mode 100644 src/net/KEFCore/Shared8/Check.cs delete mode 100644 src/net/KEFCore/Shared8/CodeAnnotations.cs delete mode 100644 src/net/KEFCore/Shared8/DictionaryExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/DisposableExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/EnumerableExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/EnumerableMethods.cs delete mode 100644 src/net/KEFCore/Shared8/ExpressionExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/Graph.cs delete mode 100644 src/net/KEFCore/Shared8/MemberInfoExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/MethodInfoExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/Multigraph.cs delete mode 100644 src/net/KEFCore/Shared8/NonCapturingLazyInitializer.cs delete mode 100644 src/net/KEFCore/Shared8/PropertyInfoExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/SharedTypeExtensions.cs delete mode 100644 src/net/KEFCore/Shared8/StringBuilderExtensions.cs diff --git a/src/net/KEFCore/KEFCore.csproj b/src/net/KEFCore/KEFCore.csproj index 80f00cc9..c1b43867 100644 --- a/src/net/KEFCore/KEFCore.csproj +++ b/src/net/KEFCore/KEFCore.csproj @@ -16,21 +16,21 @@ True False + + + + + - - - - - - - - - + + + + diff --git a/src/net/KEFCore/Shared/Check.cs b/src/net/KEFCore/Shared/Check.cs index 43b670e3..e15730ef 100644 --- a/src/net/KEFCore/Shared/Check.cs +++ b/src/net/KEFCore/Shared/Check.cs @@ -1,15 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Diagnostics; +#nullable enable + using System.Diagnostics.CodeAnalysis; -using System.Linq; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Diagnostics; - -#nullable enable namespace Microsoft.EntityFrameworkCore.Utilities; @@ -117,7 +112,17 @@ public static void DebugAssert([DoesNotReturnIf(false)] bool condition, string m { if (!condition) { +#if NET7_0_OR_GREATER + throw new UnreachableException($"Check.DebugAssert failed: {message}"); +#else throw new Exception($"Check.DebugAssert failed: {message}"); +#endif } } +#if NET7_0_OR_GREATER + [Conditional("DEBUG")] + [DoesNotReturn] + public static void DebugFail(string message) + => throw new UnreachableException($"Check.DebugFail failed: {message}"); +#endif } diff --git a/src/net/KEFCore/Shared/DictionaryExtensions.cs b/src/net/KEFCore/Shared/DictionaryExtensions.cs index bee91f23..e39f1f90 100644 --- a/src/net/KEFCore/Shared/DictionaryExtensions.cs +++ b/src/net/KEFCore/Shared/DictionaryExtensions.cs @@ -1,13 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - #nullable enable +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.EntityFrameworkCore.Utilities; [DebuggerStepThrough] @@ -35,17 +32,17 @@ public static TValue GetOrAddNew( public static bool TryGetAndRemove( this IDictionary source, TKey key, - [NotNullWhen(true)] out TReturn annotationValue) + [NotNullWhen(true)] out TReturn value) { - if (source.TryGetValue(key, out var value) - && value != null) + if (source.TryGetValue(key, out var item) + && item != null) { source.Remove(key); - annotationValue = (TReturn)(object)value; + value = (TReturn)(object)item; return true; } - annotationValue = default!; + value = default!; return false; } diff --git a/src/net/KEFCore/Shared/DisposableExtensions.cs b/src/net/KEFCore/Shared/DisposableExtensions.cs index 94c39fd8..e43c0187 100644 --- a/src/net/KEFCore/Shared/DisposableExtensions.cs +++ b/src/net/KEFCore/Shared/DisposableExtensions.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Threading.Tasks; - #nullable enable namespace Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/net/KEFCore/Shared/EnumerableExtensions.cs b/src/net/KEFCore/Shared/EnumerableExtensions.cs index 3ead63b4..eeeceea2 100644 --- a/src/net/KEFCore/Shared/EnumerableExtensions.cs +++ b/src/net/KEFCore/Shared/EnumerableExtensions.cs @@ -1,16 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - #nullable enable +using System.Collections; + // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/net/KEFCore/Shared/EnumerableMethods.cs b/src/net/KEFCore/Shared/EnumerableMethods.cs index 55229e46..9b49f693 100644 --- a/src/net/KEFCore/Shared/EnumerableMethods.cs +++ b/src/net/KEFCore/Shared/EnumerableMethods.cs @@ -1,13 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -#nullable enable namespace Microsoft.EntityFrameworkCore; @@ -17,7 +11,7 @@ internal static class EnumerableMethods //public static MethodInfo AggregateWithSeedWithoutSelector { get; } - //public static MethodInfo AggregateWithSeedSelector { get; } + public static MethodInfo AggregateWithSeedSelector { get; } public static MethodInfo All { get; } @@ -93,7 +87,7 @@ internal static class EnumerableMethods public static MethodInfo Join { get; } - //public static MethodInfo JoinWithComparer { get; } + public static MethodInfo JoinWithComparer { get; } public static MethodInfo LastWithoutPredicate { get; } @@ -202,10 +196,11 @@ internal static class EnumerableMethods //public static MethodInfo WhereOrdinal { get; } - //public static MethodInfo Zip { get; } + public static MethodInfo ZipWithSelector { get; } // private static Dictionary SumWithoutSelectorMethods { get; } private static Dictionary SumWithSelectorMethods { get; } + // private static Dictionary AverageWithoutSelectorMethods { get; } private static Dictionary AverageWithSelectorMethods { get; } private static Dictionary MaxWithoutSelectorMethods { get; } @@ -268,6 +263,16 @@ static EnumerableMethods() .GroupBy(mi => mi.Name) .ToDictionary(e => e.Key, l => l.ToList()); + AggregateWithSeedSelector = GetMethod( + nameof(Enumerable.Aggregate), 3, + types => new[] + { + typeof(IEnumerable<>).MakeGenericType(types[0]), + types[1], + typeof(Func<,,>).MakeGenericType(types[1], types[0], types[1]), + typeof(Func<,>).MakeGenericType(types[1], types[2]) + }); + All = GetMethod( nameof(Enumerable.All), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); @@ -400,6 +405,18 @@ static EnumerableMethods() typeof(Func<,,>).MakeGenericType(types[0], types[1], types[3]) }); + JoinWithComparer = GetMethod( + nameof(Enumerable.Join), 4, + types => new[] + { + typeof(IEnumerable<>).MakeGenericType(types[0]), + typeof(IEnumerable<>).MakeGenericType(types[1]), + typeof(Func<,>).MakeGenericType(types[0], types[2]), + typeof(Func<,>).MakeGenericType(types[1], types[2]), + typeof(Func<,,>).MakeGenericType(types[0], types[1], types[3]), + typeof(IEqualityComparer<>).MakeGenericType(types[2]) + }); + LastWithoutPredicate = GetMethod( nameof(Enumerable.Last), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); @@ -532,6 +549,15 @@ static EnumerableMethods() nameof(Enumerable.Where), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); + ZipWithSelector = GetMethod( + nameof(Enumerable.Zip), 3, + types => new[] + { + typeof(IEnumerable<>).MakeGenericType(types[0]), + typeof(IEnumerable<>).MakeGenericType(types[1]), + typeof(Func<,,>).MakeGenericType(types[0], types[1], types[2]) + }); + var numericTypes = new[] { typeof(int), diff --git a/src/net/KEFCore/Shared/ExpressionExtensions.cs b/src/net/KEFCore/Shared/ExpressionExtensions.cs index 10f64a62..0835bd7a 100644 --- a/src/net/KEFCore/Shared/ExpressionExtensions.cs +++ b/src/net/KEFCore/Shared/ExpressionExtensions.cs @@ -1,11 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - #nullable enable +using System.Diagnostics.CodeAnalysis; + // ReSharper disable once CheckNamespace namespace System.Linq.Expressions; @@ -13,8 +12,7 @@ namespace System.Linq.Expressions; internal static class ExpressionExtensions { public static bool IsNullConstantExpression(this Expression expression) - => RemoveConvert(expression) is ConstantExpression constantExpression - && constantExpression.Value == null; + => RemoveConvert(expression) is ConstantExpression { Value: null }; public static LambdaExpression UnwrapLambdaFromQuote(this Expression expression) => (LambdaExpression)(expression is UnaryExpression unary && expression.NodeType == ExpressionType.Quote @@ -25,10 +23,10 @@ public static LambdaExpression UnwrapLambdaFromQuote(this Expression expression) public static Expression? UnwrapTypeConversion(this Expression? expression, out Type? convertedType) { convertedType = null; - while (expression is UnaryExpression unaryExpression - && (unaryExpression.NodeType == ExpressionType.Convert - || unaryExpression.NodeType == ExpressionType.ConvertChecked - || unaryExpression.NodeType == ExpressionType.TypeAs)) + while (expression is UnaryExpression + { + NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs + } unaryExpression) { expression = unaryExpression.Operand; if (unaryExpression.Type != typeof(object) // Ignore object conversion @@ -42,16 +40,9 @@ public static LambdaExpression UnwrapLambdaFromQuote(this Expression expression) } private static Expression RemoveConvert(Expression expression) - { - if (expression is UnaryExpression unaryExpression - && (expression.NodeType == ExpressionType.Convert - || expression.NodeType == ExpressionType.ConvertChecked)) - { - return RemoveConvert(unaryExpression.Operand); - } - - return expression; - } + => expression is UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } unaryExpression + ? RemoveConvert(unaryExpression.Operand) + : expression; public static T GetConstantValue(this Expression expression) => expression is ConstantExpression constantExpression diff --git a/src/net/KEFCore/Shared8/ExpressionVisitorExtensions.cs b/src/net/KEFCore/Shared/ExpressionVisitorExtensions.cs similarity index 100% rename from src/net/KEFCore/Shared8/ExpressionVisitorExtensions.cs rename to src/net/KEFCore/Shared/ExpressionVisitorExtensions.cs diff --git a/src/net/KEFCore/Shared/Graph.cs b/src/net/KEFCore/Shared/Graph.cs index 1577b19d..0fc0d1ae 100644 --- a/src/net/KEFCore/Shared/Graph.cs +++ b/src/net/KEFCore/Shared/Graph.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Linq; - -#nullable enable - namespace Microsoft.EntityFrameworkCore.Utilities; internal abstract class Graph diff --git a/src/net/KEFCore/Shared8/HashHelpers.cs b/src/net/KEFCore/Shared/HashHelpers.cs similarity index 100% rename from src/net/KEFCore/Shared8/HashHelpers.cs rename to src/net/KEFCore/Shared/HashHelpers.cs diff --git a/src/net/KEFCore/Shared8/IDictionaryDebugView.cs b/src/net/KEFCore/Shared/IDictionaryDebugView.cs similarity index 100% rename from src/net/KEFCore/Shared8/IDictionaryDebugView.cs rename to src/net/KEFCore/Shared/IDictionaryDebugView.cs diff --git a/src/net/KEFCore/Shared/MemberInfoExtensions.cs b/src/net/KEFCore/Shared/MemberInfoExtensions.cs index c96d2964..06564f5c 100644 --- a/src/net/KEFCore/Shared/MemberInfoExtensions.cs +++ b/src/net/KEFCore/Shared/MemberInfoExtensions.cs @@ -1,14 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Linq; - #nullable enable namespace System.Reflection; internal static class EntityFrameworkMemberInfoExtensions - { public static Type GetMemberType(this MemberInfo memberInfo) => (memberInfo as PropertyInfo)?.PropertyType ?? ((FieldInfo)memberInfo).FieldType; @@ -28,7 +25,7 @@ public static bool IsSameAs(this MemberInfo? propertyInfo, MemberInfo? otherProp || otherPropertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces .Contains(propertyInfo.DeclaringType))))); - public static bool IsOverridenBy(this MemberInfo? propertyInfo, MemberInfo? otherPropertyInfo) + public static bool IsOverriddenBy(this MemberInfo? propertyInfo, MemberInfo? otherPropertyInfo) => propertyInfo == null ? otherPropertyInfo == null : (otherPropertyInfo != null @@ -49,5 +46,5 @@ public static string GetSimpleMemberName(this MemberInfo member) } public static bool IsReallyVirtual(this MethodInfo method) - => method.IsVirtual && !method.IsFinal; + => method is { IsVirtual: true, IsFinal: false }; } diff --git a/src/net/KEFCore/Shared/MethodInfoExtensions.cs b/src/net/KEFCore/Shared/MethodInfoExtensions.cs index d49a51a2..1d86c668 100644 --- a/src/net/KEFCore/Shared/MethodInfoExtensions.cs +++ b/src/net/KEFCore/Shared/MethodInfoExtensions.cs @@ -2,19 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; - -#nullable enable namespace System.Reflection; internal static class MethodInfoExtensions { public static bool IsContainsMethod(this MethodInfo method) - => method.Name == nameof(IList.Contains) - && method.DeclaringType != null + => method is { Name: nameof(IList.Contains), DeclaringType: not null } && method.DeclaringType.GetInterfaces().Append(method.DeclaringType).Any( t => t == typeof(IList) || (t.IsGenericType diff --git a/src/net/KEFCore/Shared/Multigraph.cs b/src/net/KEFCore/Shared/Multigraph.cs index f86a8317..ff803950 100644 --- a/src/net/KEFCore/Shared/Multigraph.cs +++ b/src/net/KEFCore/Shared/Multigraph.cs @@ -8,9 +8,24 @@ namespace Microsoft.EntityFrameworkCore.Utilities; internal class Multigraph : Graph where TVertex : notnull { + private readonly IComparer? _secondarySortComparer; private readonly HashSet _vertices = new(); private readonly Dictionary> _successorMap = new(); - private readonly Dictionary> _predecessorMap = new(); + private readonly Dictionary> _predecessorMap = new(); + + public Multigraph() + { + } + + public Multigraph(IComparer secondarySortComparer) + { + _secondarySortComparer = secondarySortComparer; + } + + public Multigraph(Comparison secondarySortComparer) + : this(Comparer.Create(secondarySortComparer)) + { + } public IEnumerable GetEdges(TVertex from, TVertex to) { @@ -18,7 +33,7 @@ public IEnumerable GetEdges(TVertex from, TVertex to) { if (successorSet.TryGetValue(to, out var edges)) { - return edges is IEnumerable edgeList ? edgeList : (new[] { (TEdge)edges! }); + return edges is IEnumerable edgeList ? edgeList.Select(e => e.Payload) : (new[] { ((Edge)edges!).Payload }); } } @@ -31,7 +46,7 @@ public void AddVertex(TVertex vertex) public void AddVertices(IEnumerable vertices) => _vertices.UnionWith(vertices); - public void AddEdge(TVertex from, TVertex to, TEdge edge) + public void AddEdge(TVertex from, TVertex to, TEdge payload, bool requiresBatchingBoundary = false) { #if DEBUG if (!_vertices.Contains(from)) @@ -45,6 +60,8 @@ public void AddEdge(TVertex from, TVertex to, TEdge edge) } #endif + var edge = new Edge(payload, requiresBatchingBoundary); + if (!_successorMap.TryGetValue(from, out var successorEdges)) { successorEdges = new Dictionary(); @@ -53,9 +70,9 @@ public void AddEdge(TVertex from, TVertex to, TEdge edge) if (successorEdges.TryGetValue(to, out var edges)) { - if (edges is not List edgeList) + if (edges is not List edgeList) { - edgeList = new List { (TEdge)edges! }; + edgeList = new List { (Edge)edges! }; successorEdges[to] = edgeList; } @@ -66,13 +83,26 @@ public void AddEdge(TVertex from, TVertex to, TEdge edge) successorEdges.Add(to, edge); } - if (!_predecessorMap.TryGetValue(to, out var predecessors)) + if (!_predecessorMap.TryGetValue(to, out var predecessorEdges)) { - predecessors = new HashSet(); - _predecessorMap.Add(to, predecessors); + predecessorEdges = new Dictionary(); + _predecessorMap.Add(to, predecessorEdges); } - predecessors.Add(from); + if (predecessorEdges.TryGetValue(from, out edges)) + { + if (edges is not List edgeList) + { + edgeList = new List { (Edge)edges! }; + predecessorEdges[from] = edgeList; + } + + edgeList.Add(edge); + } + else + { + predecessorEdges.Add(from, edge); + } } public override void Clear() @@ -98,41 +128,108 @@ public IReadOnlyList TopologicalSort( Func>>, string>? formatCycle, Func? formatException = null) { - var queue = new List(); + var batches = TopologicalSortCore(withBatching: false, tryBreakEdge, formatCycle, formatException); + + Check.DebugAssert(batches.Count < 2, "TopologicalSortCore did batching but withBatching was false"); + + return batches.Count == 1 + ? batches[0] + : Array.Empty(); + } + + protected virtual string? ToString(TVertex vertex) + => vertex.ToString(); + + public IReadOnlyList> BatchingTopologicalSort() + => BatchingTopologicalSort(null, null); + + public IReadOnlyList> BatchingTopologicalSort( + Func, bool>? canBreakEdges, + Func>>, string>? formatCycle, + Func? formatException = null) + => TopologicalSortCore(withBatching: true, canBreakEdges, formatCycle, formatException); + + private IReadOnlyList> TopologicalSortCore( + bool withBatching, + Func, bool>? canBreakEdges, + Func>>, string>? formatCycle, + Func? formatException = null) + { + // Performs a breadth-first topological sort (Kahn's algorithm) + var result = new List>(); + var currentRootsQueue = new List(); + var nextRootsQueue = new List(); + var vertexesProcessed = 0; + var batchBoundaryRequired = false; + var currentBatch = new List(); + var currentBatchSet = new HashSet(); + var predecessorCounts = new Dictionary(_predecessorMap.Count); foreach (var (vertex, vertices) in _predecessorMap) { predecessorCounts[vertex] = vertices.Count; } + // Bootstrap the topological sort by finding all vertexes which have no predecessors foreach (var vertex in _vertices) { if (!predecessorCounts.ContainsKey(vertex)) { - queue.Add(vertex); + currentRootsQueue.Add(vertex); } } - var index = 0; - while (queue.Count < _vertices.Count) + result.Add(currentBatch); + + while (vertexesProcessed < _vertices.Count) { - while (index < queue.Count) + while (currentRootsQueue.Count > 0) { - var currentRoot = queue[index]; - index++; + // Secondary sorting: after the first topological sorting (according to dependencies between the commands as expressed in + // the graph), we apply an optional secondary sort. + // When sorting modification commands, this ensures a deterministic ordering and prevents deadlocks between concurrent + // transactions locking the same rows in different orders. + if (_secondarySortComparer is not null) + { + currentRootsQueue.Sort(_secondarySortComparer); + } + + // If we detected in the last roots pass that a batch boundary is required, close the current batch and start a new one. + if (batchBoundaryRequired) + { + currentBatch = new List(); + result.Add(currentBatch); + currentBatchSet.Clear(); + + batchBoundaryRequired = false; + } - foreach (var successor in GetOutgoingNeighbors(currentRoot)) + foreach (var currentRoot in currentRootsQueue) { - predecessorCounts[successor]--; - if (predecessorCounts[successor] == 0) + currentBatch.Add(currentRoot); + currentBatchSet.Add(currentRoot); + vertexesProcessed++; + + foreach (var successor in GetOutgoingNeighbors(currentRoot)) { - queue.Add(successor); + predecessorCounts[successor]--; + + // If the successor has no other predecessors, add it for processing in the next roots pass. + if (predecessorCounts[successor] == 0) + { + nextRootsQueue.Add(successor); + CheckBatchingBoundary(successor); + } } } + + // Finished passing over the current roots, move on to the next set. + (currentRootsQueue, nextRootsQueue) = (nextRootsQueue, currentRootsQueue); + nextRootsQueue.Clear(); } - // Cycle breaking - if (queue.Count < _vertices.Count) + // We have no more roots to process. That either means we're done, or that there's a cycle which we need to break + if (vertexesProcessed < _vertices.Count) { var broken = false; @@ -141,7 +238,7 @@ public IReadOnlyList TopologicalSort( while ((candidateIndex < candidateVertices.Count) && !broken - && tryBreakEdge != null) + && canBreakEdges != null) { var candidateVertex = candidateVertices[candidateIndex]; if (predecessorCounts[candidateVertex] == 0) @@ -156,14 +253,18 @@ public IReadOnlyList TopologicalSort( neighbor => predecessorCounts.TryGetValue(neighbor, out var neighborPredecessors) && neighborPredecessors > 0); - if (tryBreakEdge(incomingNeighbor, candidateVertex, GetEdges(incomingNeighbor, candidateVertex))) + if (canBreakEdges(incomingNeighbor, candidateVertex, GetEdges(incomingNeighbor, candidateVertex))) { - _successorMap[incomingNeighbor].Remove(candidateVertex); - _predecessorMap[candidateVertex].Remove(incomingNeighbor); + var removed = _successorMap[incomingNeighbor].Remove(candidateVertex); + Check.DebugAssert(removed, "Candidate vertex not found in successor map"); + removed = _predecessorMap[candidateVertex].Remove(incomingNeighbor); + Check.DebugAssert(removed, "Incoming neighbor not found in predecessor map"); + predecessorCounts[candidateVertex]--; if (predecessorCounts[candidateVertex] == 0) { - queue.Add(candidateVertex); + currentRootsQueue.Add(candidateVertex); + CheckBatchingBoundary(candidateVertex); broken = true; } @@ -219,7 +320,25 @@ public IReadOnlyList TopologicalSort( } } - return queue; + return result; + + // Detect batch boundary (if batching is enabled). + // If the successor has any predecessor where the edge requires a batching boundary, and that predecessor is + // already in the current batch, then the next batch will have to be executed in a separate batch. + // TODO: Optimization: Instead of currentBatchSet, store a batch counter on each vertex, and check if later + // vertexes have a boundary-requiring dependency on a vertex with the same batch counter. + void CheckBatchingBoundary(TVertex vertex) + { + if (withBatching + && _predecessorMap[vertex].Any( + kv => + (kv.Value is Edge { RequiresBatchingBoundary: true } + || kv.Value is IEnumerable edges && edges.Any(e => e.RequiresBatchingBoundary)) + && currentBatchSet.Contains(kv.Key))) + { + batchBoundaryRequired = true; + } + } } private void ThrowCycle( @@ -250,158 +369,6 @@ private void ThrowCycle( throw new InvalidOperationException(message); } - protected virtual string? ToString(TVertex vertex) - => vertex.ToString(); - - public IReadOnlyList> BatchingTopologicalSort() - => BatchingTopologicalSort(null, null); - - public IReadOnlyList> BatchingTopologicalSort( - Func, bool>? tryBreakEdge, - Func>>, string>? formatCycle) - { - var currentRootsQueue = new List(); - var predecessorCounts = new Dictionary(_predecessorMap.Count); - foreach (var (vertex, vertices) in _predecessorMap) - { - predecessorCounts[vertex] = vertices.Count; - } - - foreach (var vertex in _vertices) - { - if (!predecessorCounts.ContainsKey(vertex)) - { - currentRootsQueue.Add(vertex); - } - } - - var result = new List>(); - var nextRootsQueue = new List(); - - while (result.Sum(b => b.Count) != _vertices.Count) - { - var currentRootIndex = 0; - while (currentRootIndex < currentRootsQueue.Count) - { - var currentRoot = currentRootsQueue[currentRootIndex]; - currentRootIndex++; - - foreach (var successor in GetOutgoingNeighbors(currentRoot)) - { - predecessorCounts[successor]--; - if (predecessorCounts[successor] == 0) - { - nextRootsQueue.Add(successor); - } - } - - // Roll lists over for next batch - if (currentRootIndex == currentRootsQueue.Count) - { - result.Add(currentRootsQueue); - - currentRootsQueue = nextRootsQueue; - currentRootIndex = 0; - - if (currentRootsQueue.Count != 0) - { - nextRootsQueue = new List(); - } - } - } - - // Cycle breaking - if (result.Sum(b => b.Count) != _vertices.Count) - { - var broken = false; - - var candidateVertices = predecessorCounts.Keys.ToList(); - var candidateIndex = 0; - - while ((candidateIndex < candidateVertices.Count) - && !broken - && tryBreakEdge != null) - { - var candidateVertex = candidateVertices[candidateIndex]; - if (predecessorCounts[candidateVertex] == 0) - { - candidateIndex++; - continue; - } - - // Find a vertex in the unsorted portion of the graph that has edges to the candidate - var incomingNeighbor = GetIncomingNeighbors(candidateVertex) - .First( - neighbor => predecessorCounts.TryGetValue(neighbor, out var neighborPredecessors) - && neighborPredecessors > 0); - - if (tryBreakEdge(incomingNeighbor, candidateVertex, GetEdges(incomingNeighbor, candidateVertex))) - { - _successorMap[incomingNeighbor].Remove(candidateVertex); - _predecessorMap[candidateVertex].Remove(incomingNeighbor); - predecessorCounts[candidateVertex]--; - if (predecessorCounts[candidateVertex] == 0) - { - currentRootsQueue.Add(candidateVertex); - nextRootsQueue = new List(); - broken = true; - } - - continue; - } - - candidateIndex++; - } - - if (broken) - { - continue; - } - - var currentCycleVertex = _vertices.First( - v => predecessorCounts.TryGetValue(v, out var predecessorCount) && predecessorCount != 0); - var cycle = new List { currentCycleVertex }; - var finished = false; - while (!finished) - { - foreach (var predecessor in GetIncomingNeighbors(currentCycleVertex)) - { - if (!predecessorCounts.TryGetValue(predecessor, out var predecessorCount) - || predecessorCount == 0) - { - continue; - } - - predecessorCounts[currentCycleVertex] = -1; - - currentCycleVertex = predecessor; - cycle.Add(currentCycleVertex); - finished = predecessorCounts[predecessor] == -1; - break; - } - } - - cycle.Reverse(); - - // Remove any tail that's not part of the cycle - var startingVertex = cycle[0]; - for (var i = cycle.Count - 1; i >= 0; i--) - { - if (cycle[i].Equals(startingVertex)) - { - break; - } - - cycle.RemoveAt(i); - } - - ThrowCycle(cycle, formatCycle); - } - } - - return result; - } - public override IEnumerable Vertices => _vertices; @@ -412,6 +379,8 @@ public override IEnumerable GetOutgoingNeighbors(TVertex from) public override IEnumerable GetIncomingNeighbors(TVertex to) => _predecessorMap.TryGetValue(to, out var predecessors) - ? predecessors + ? predecessors.Keys : Enumerable.Empty(); + + private record struct Edge(TEdge Payload, bool RequiresBatchingBoundary); } diff --git a/src/net/KEFCore/Shared/NonCapturingLazyInitializer.cs b/src/net/KEFCore/Shared/NonCapturingLazyInitializer.cs index 0d81db71..42412bda 100644 --- a/src/net/KEFCore/Shared/NonCapturingLazyInitializer.cs +++ b/src/net/KEFCore/Shared/NonCapturingLazyInitializer.cs @@ -1,13 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; +#nullable enable + using System.Diagnostics.CodeAnalysis; -using System.Threading; using Microsoft.EntityFrameworkCore.Utilities; -#nullable enable - namespace Microsoft.EntityFrameworkCore.Internal; internal static class NonCapturingLazyInitializer diff --git a/src/net/KEFCore/Shared8/OrderedDictionary.KeyCollection.cs b/src/net/KEFCore/Shared/OrderedDictionary.KeyCollection.cs similarity index 100% rename from src/net/KEFCore/Shared8/OrderedDictionary.KeyCollection.cs rename to src/net/KEFCore/Shared/OrderedDictionary.KeyCollection.cs diff --git a/src/net/KEFCore/Shared8/OrderedDictionary.ValueCollection.cs b/src/net/KEFCore/Shared/OrderedDictionary.ValueCollection.cs similarity index 100% rename from src/net/KEFCore/Shared8/OrderedDictionary.ValueCollection.cs rename to src/net/KEFCore/Shared/OrderedDictionary.ValueCollection.cs diff --git a/src/net/KEFCore/Shared8/OrderedDictionary.cs b/src/net/KEFCore/Shared/OrderedDictionary.cs similarity index 100% rename from src/net/KEFCore/Shared8/OrderedDictionary.cs rename to src/net/KEFCore/Shared/OrderedDictionary.cs diff --git a/src/net/KEFCore/Shared/PropertyInfoExtensions.cs b/src/net/KEFCore/Shared/PropertyInfoExtensions.cs index 021aaa95..e93d8c47 100644 --- a/src/net/KEFCore/Shared/PropertyInfoExtensions.cs +++ b/src/net/KEFCore/Shared/PropertyInfoExtensions.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Linq; - #nullable enable // ReSharper disable once CheckNamespace + namespace System.Reflection; [DebuggerStepThrough] @@ -15,13 +13,16 @@ internal static class PropertyInfoExtensions public static bool IsStatic(this PropertyInfo property) => (property.GetMethod ?? property.SetMethod)!.IsStatic; - public static bool IsCandidateProperty(this PropertyInfo propertyInfo, bool needsWrite = true, bool publicOnly = true) - => !propertyInfo.IsStatic() + public static bool IsCandidateProperty(this MemberInfo memberInfo, bool needsWrite = true, bool publicOnly = true) + => memberInfo is PropertyInfo propertyInfo + ? !propertyInfo.IsStatic() && propertyInfo.CanRead && (!needsWrite || propertyInfo.FindSetterProperty() != null) && propertyInfo.GetMethod != null && (!publicOnly || propertyInfo.GetMethod.IsPublic) - && propertyInfo.GetIndexParameters().Length == 0; + && propertyInfo.GetIndexParameters().Length == 0 + : memberInfo is FieldInfo { IsStatic: false } fieldInfo + && (!publicOnly || fieldInfo.IsPublic); public static bool IsIndexerProperty(this PropertyInfo propertyInfo) { diff --git a/src/net/KEFCore/Shared8/SharedStopwatch.cs b/src/net/KEFCore/Shared/SharedStopwatch.cs similarity index 100% rename from src/net/KEFCore/Shared8/SharedStopwatch.cs rename to src/net/KEFCore/Shared/SharedStopwatch.cs diff --git a/src/net/KEFCore/Shared/SharedTypeExtensions.cs b/src/net/KEFCore/Shared/SharedTypeExtensions.cs index c3574f18..4a116ef6 100644 --- a/src/net/KEFCore/Shared/SharedTypeExtensions.cs +++ b/src/net/KEFCore/Shared/SharedTypeExtensions.cs @@ -1,16 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; +#nullable enable + +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text; -#nullable enable - // ReSharper disable once CheckNamespace namespace System; @@ -47,10 +43,19 @@ public static bool IsNullableType(this Type type) => !type.IsValueType || type.IsNullableValueType(); public static bool IsValidEntityType(this Type type) - => type.IsClass - && !type.IsArray; + => type is { IsClass: true, IsArray: false } + && type != typeof(string); - public static bool IsPropertyBagType(this Type type) + public static bool IsValidComplexType(this Type type) + => !type.IsArray + && !type.IsInterface + && !IsScalarType(type); + + public static bool IsScalarType(this Type type) + => type == typeof(string) + || CommonTypeDictionary.ContainsKey(type); + + public static bool IsPropertyBagType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { if (type.IsGenericTypeDefinition) { @@ -106,7 +111,10 @@ public static bool IsAnonymousType(this Type type) && type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Length > 0 && type.Name.Contains("AnonymousType"); - public static PropertyInfo? GetAnyProperty(this Type type, string name) + public static PropertyInfo? GetAnyProperty( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + this Type type, + string name) { var props = type.GetRuntimeProperties().Where(p => p.Name == name).ToList(); if (props.Count > 1) @@ -118,8 +126,7 @@ public static bool IsAnonymousType(this Type type) } public static bool IsInstantiable(this Type type) - => !type.IsAbstract - && !type.IsInterface + => type is { IsAbstract: false, IsInterface: false } && (!type.IsGenericType || !type.IsGenericTypeDefinition); public static Type UnwrapEnumType(this Type type) @@ -135,7 +142,7 @@ public static Type UnwrapEnumType(this Type type) return isNullable ? MakeNullable(underlyingEnumType) : underlyingEnumType; } - public static Type GetSequenceType(this Type type) + public static Type GetSequenceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { var sequenceType = TryGetSequenceType(type); if (sequenceType == null) @@ -146,11 +153,13 @@ public static Type GetSequenceType(this Type type) return sequenceType; } - public static Type? TryGetSequenceType(this Type type) + public static Type? TryGetSequenceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) => type.TryGetElementType(typeof(IEnumerable<>)) ?? type.TryGetElementType(typeof(IAsyncEnumerable<>)); - public static Type? TryGetElementType(this Type type, Type interfaceOrBaseType) + public static Type? TryGetElementType( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type, + Type interfaceOrBaseType) { if (type.IsGenericTypeDefinition) { @@ -250,8 +259,7 @@ public static List GetBaseTypesAndInterfacesInclusive(this Type type) typesToProcess.Enqueue(type.GetGenericTypeDefinition()); } - if (!type.IsGenericTypeDefinition - && !type.IsInterface) + if (type is { IsGenericTypeDefinition: false, IsInterface: false }) { if (type.BaseType != null) { @@ -280,7 +288,8 @@ public static IEnumerable GetTypesInHierarchy(this Type type) } } - public static IEnumerable GetDeclaredInterfaces(this Type type) + public static IEnumerable GetDeclaredInterfaces( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { var interfaces = type.GetInterfaces(); if (type.BaseType == typeof(object) @@ -289,10 +298,18 @@ public static IEnumerable GetDeclaredInterfaces(this Type type) return interfaces; } - return interfaces.Except(type.BaseType.GetInterfaces()); + return interfaces.Except(GetInterfacesSuppressed(type.BaseType)); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070", Justification = "https://github.com/dotnet/linker/issues/2473")] + static IEnumerable GetInterfacesSuppressed(Type type) + => type.GetInterfaces(); } - public static ConstructorInfo? GetDeclaredConstructor(this Type type, Type[]? types) + public static ConstructorInfo? GetDeclaredConstructor( + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + this Type type, + Type[]? types) { types ??= Array.Empty(); @@ -345,11 +362,19 @@ public static IEnumerable GetMembersInHierarchy(this Type type) while (currentType != null); } - public static IEnumerable GetMembersInHierarchy(this Type type, string name) + public static IEnumerable GetMembersInHierarchy( + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields)] + this Type type, + string name) => type.GetMembersInHierarchy().Where(m => m.Name == name); private static readonly Dictionary CommonTypeDictionary = new() { +#pragma warning disable IDE0034 // Simplify 'default' expression - default causes default(object) { typeof(int), default(int) }, { typeof(Guid), default(Guid) }, { typeof(DateOnly), default(DateOnly) }, @@ -367,9 +392,12 @@ public static IEnumerable GetMembersInHierarchy(this Type type, stri { typeof(ushort), default(ushort) }, { typeof(ulong), default(ulong) }, { typeof(sbyte), default(sbyte) } +#pragma warning restore IDE0034 // Simplify 'default' expression }; - public static object? GetDefaultValue(this Type type) + public static object? GetDefaultValue( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + this Type type) { if (!type.IsValueType) { @@ -384,11 +412,12 @@ public static IEnumerable GetMembersInHierarchy(this Type type, stri : Activator.CreateInstance(type); } + [RequiresUnreferencedCode("Gets all types from the given assembly - unsafe for trimming")] public static IEnumerable GetConstructibleTypes(this Assembly assembly) => assembly.GetLoadableDefinedTypes().Where( - t => !t.IsAbstract - && !t.IsGenericTypeDefinition); + t => t is { IsAbstract: false, IsGenericTypeDefinition: false }); + [RequiresUnreferencedCode("Gets all types from the given assembly - unsafe for trimming")] public static IEnumerable GetLoadableDefinedTypes(this Assembly assembly) { try @@ -554,6 +583,16 @@ public static IEnumerable GetNamespaces(this Type type) yield break; } + if (type.IsArray) + { + foreach (var ns in type.GetElementType()!.GetNamespaces()) + { + yield return ns; + } + + yield break; + } + yield return type.Namespace!; if (type.IsGenericType) diff --git a/src/net/KEFCore/Shared/StringBuilderExtensions.cs b/src/net/KEFCore/Shared/StringBuilderExtensions.cs index 12f15147..0e0b253f 100644 --- a/src/net/KEFCore/Shared/StringBuilderExtensions.cs +++ b/src/net/KEFCore/Shared/StringBuilderExtensions.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Globalization; -#nullable enable - namespace System.Text; internal static class StringBuilderExtensions diff --git a/src/net/KEFCore/Shared8/Check.cs b/src/net/KEFCore/Shared8/Check.cs deleted file mode 100644 index 2ebf5ec1..00000000 --- a/src/net/KEFCore/Shared8/Check.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Diagnostics.CodeAnalysis; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Utilities; - -[DebuggerStepThrough] -internal static class Check -{ - [ContractAnnotation("value:null => halt")] - [return: NotNull] - public static T NotNull([NoEnumeration] [AllowNull] [NotNull] T value, [InvokerParameterName] string parameterName) - { - if (value is null) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentNullException(parameterName); - } - - return value; - } - - [ContractAnnotation("value:null => halt")] - public static IReadOnlyList NotEmpty( - [NotNull] IReadOnlyList? value, - [InvokerParameterName] string parameterName) - { - NotNull(value, parameterName); - - if (value.Count == 0) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentException(AbstractionsStrings.CollectionArgumentIsEmpty(parameterName)); - } - - return value; - } - - [ContractAnnotation("value:null => halt")] - public static string NotEmpty([NotNull] string? value, [InvokerParameterName] string parameterName) - { - if (value is null) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentNullException(parameterName); - } - - if (value.Trim().Length == 0) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName)); - } - - return value; - } - - public static string? NullButNotEmpty(string? value, [InvokerParameterName] string parameterName) - { - if (value is not null && value.Length == 0) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentException(AbstractionsStrings.ArgumentIsEmpty(parameterName)); - } - - return value; - } - - public static IReadOnlyList HasNoNulls( - [NotNull] IReadOnlyList? value, - [InvokerParameterName] string parameterName) - where T : class - { - NotNull(value, parameterName); - - if (value.Any(e => e == null)) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentException(parameterName); - } - - return value; - } - - public static IReadOnlyList HasNoEmptyElements( - [NotNull] IReadOnlyList? value, - [InvokerParameterName] string parameterName) - { - NotNull(value, parameterName); - - if (value.Any(s => string.IsNullOrWhiteSpace(s))) - { - NotEmpty(parameterName, nameof(parameterName)); - - throw new ArgumentException(AbstractionsStrings.CollectionArgumentHasEmptyElements(parameterName)); - } - - return value; - } - - [Conditional("DEBUG")] - public static void DebugAssert([DoesNotReturnIf(false)] bool condition, string message) - { - if (!condition) - { - throw new UnreachableException($"Check.DebugAssert failed: {message}"); - } - } - - [Conditional("DEBUG")] - [DoesNotReturn] - public static void DebugFail(string message) - => throw new UnreachableException($"Check.DebugFail failed: {message}"); -} diff --git a/src/net/KEFCore/Shared8/CodeAnnotations.cs b/src/net/KEFCore/Shared8/CodeAnnotations.cs deleted file mode 100644 index d81a2c26..00000000 --- a/src/net/KEFCore/Shared8/CodeAnnotations.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -#nullable enable - -namespace JetBrains.Annotations; - -[AttributeUsage(AttributeTargets.Parameter)] -internal sealed class InvokerParameterNameAttribute : Attribute -{ -} - -[AttributeUsage(AttributeTargets.Parameter)] -internal sealed class NoEnumerationAttribute : Attribute -{ -} - -[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] -internal sealed class ContractAnnotationAttribute : Attribute -{ - public string Contract { get; } - - public bool ForceFullStates { get; } - - public ContractAnnotationAttribute(string contract) - : this(contract, false) - { - } - - public ContractAnnotationAttribute(string contract, bool forceFullStates) - { - Contract = contract; - ForceFullStates = forceFullStates; - } -} - -[AttributeUsage(AttributeTargets.All)] -internal sealed class UsedImplicitlyAttribute : Attribute -{ - public UsedImplicitlyAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) - { - } - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) - { - } - - public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) - { - } - - public UsedImplicitlyAttribute( - ImplicitUseKindFlags useKindFlags, - ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } - - public ImplicitUseKindFlags UseKindFlags { get; } - public ImplicitUseTargetFlags TargetFlags { get; } -} - -[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)] -internal sealed class StringFormatMethodAttribute : Attribute -{ - public StringFormatMethodAttribute(string formatParameterName) - { - FormatParameterName = formatParameterName; - } - - public string FormatParameterName { get; } -} - -[Flags] -internal enum ImplicitUseKindFlags -{ - Default = Access | Assign | InstantiatedWithFixedConstructorSignature, - Access = 1, - Assign = 2, - InstantiatedWithFixedConstructorSignature = 4, - InstantiatedNoFixedConstructorSignature = 8 -} - -[Flags] -internal enum ImplicitUseTargetFlags -{ - Default = Itself, - Itself = 1, - Members = 2, - WithMembers = Itself | Members -} diff --git a/src/net/KEFCore/Shared8/DictionaryExtensions.cs b/src/net/KEFCore/Shared8/DictionaryExtensions.cs deleted file mode 100644 index e39f1f90..00000000 --- a/src/net/KEFCore/Shared8/DictionaryExtensions.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.EntityFrameworkCore.Utilities; - -[DebuggerStepThrough] -internal static class DictionaryExtensions -{ - public static TValue GetOrAddNew( - this IDictionary source, - TKey key) - where TValue : new() - { - if (!source.TryGetValue(key, out var value)) - { - value = new TValue(); - source.Add(key, value); - } - - return value; - } - - public static TValue? Find( - this IReadOnlyDictionary source, - TKey key) - => !source.TryGetValue(key, out var value) ? default : value; - - public static bool TryGetAndRemove( - this IDictionary source, - TKey key, - [NotNullWhen(true)] out TReturn value) - { - if (source.TryGetValue(key, out var item) - && item != null) - { - source.Remove(key); - value = (TReturn)(object)item; - return true; - } - - value = default!; - return false; - } - - public static void Remove( - this IDictionary source, - Func predicate) - => source.Remove((k, v, p) => p!(k, v), predicate); - - public static void Remove( - this IDictionary source, - Func predicate, - TState? state) - { - var found = false; - var firstRemovedKey = default(TKey); - List>? pairsRemainder = null; - foreach (var pair in source) - { - if (found) - { - pairsRemainder ??= new List>(); - - pairsRemainder.Add(pair); - continue; - } - - if (!predicate(pair.Key, pair.Value, state)) - { - continue; - } - - if (!found) - { - found = true; - firstRemovedKey = pair.Key; - } - } - - if (found) - { - source.Remove(firstRemovedKey!); - if (pairsRemainder == null) - { - return; - } - - foreach (var (key, value) in pairsRemainder) - { - if (predicate(key, value, state)) - { - source.Remove(key); - } - } - } - } -} diff --git a/src/net/KEFCore/Shared8/DisposableExtensions.cs b/src/net/KEFCore/Shared8/DisposableExtensions.cs deleted file mode 100644 index e43c0187..00000000 --- a/src/net/KEFCore/Shared8/DisposableExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -namespace Microsoft.EntityFrameworkCore.Utilities; - -internal static class DisposableExtensions -{ - public static ValueTask DisposeAsyncIfAvailable(this IDisposable? disposable) - { - if (disposable != null) - { - if (disposable is IAsyncDisposable asyncDisposable) - { - return asyncDisposable.DisposeAsync(); - } - - disposable.Dispose(); - } - - return default; - } -} diff --git a/src/net/KEFCore/Shared8/EnumerableExtensions.cs b/src/net/KEFCore/Shared8/EnumerableExtensions.cs deleted file mode 100644 index eeeceea2..00000000 --- a/src/net/KEFCore/Shared8/EnumerableExtensions.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Collections; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore.Utilities; - -[DebuggerStepThrough] -internal static class EnumerableExtensions -{ - public static IOrderedEnumerable OrderByOrdinal( - this IEnumerable source, - Func keySelector) - => source.OrderBy(keySelector, StringComparer.Ordinal); - - public static IEnumerable Distinct( - this IEnumerable source, - Func comparer) - where T : class - => source.Distinct(new DynamicEqualityComparer(comparer)); - - private sealed class DynamicEqualityComparer : IEqualityComparer - where T : class - { - private readonly Func _func; - - public DynamicEqualityComparer(Func func) - { - _func = func; - } - - public bool Equals(T? x, T? y) - => _func(x, y); - - public int GetHashCode(T obj) - => 0; - } - - public static string Join( - this IEnumerable source, - string separator = ", ") - => string.Join(separator, source); - - public static bool StructuralSequenceEqual( - this IEnumerable first, - IEnumerable second) - { - if (ReferenceEquals(first, second)) - { - return true; - } - - using var firstEnumerator = first.GetEnumerator(); - using var secondEnumerator = second.GetEnumerator(); - while (firstEnumerator.MoveNext()) - { - if (!secondEnumerator.MoveNext() - || !StructuralComparisons.StructuralEqualityComparer - .Equals(firstEnumerator.Current, secondEnumerator.Current)) - { - return false; - } - } - - return !secondEnumerator.MoveNext(); - } - - public static bool StartsWith( - this IEnumerable first, - IEnumerable second) - { - if (ReferenceEquals(first, second)) - { - return true; - } - - using var firstEnumerator = first.GetEnumerator(); - using var secondEnumerator = second.GetEnumerator(); - - while (secondEnumerator.MoveNext()) - { - if (!firstEnumerator.MoveNext() - || !Equals(firstEnumerator.Current, secondEnumerator.Current)) - { - return false; - } - } - - return true; - } - - public static int IndexOf(this IEnumerable source, T item) - => IndexOf(source, item, EqualityComparer.Default); - - public static int IndexOf( - this IEnumerable source, - T item, - IEqualityComparer comparer) - => source.Select( - (x, index) => - comparer.Equals(item, x) ? index : -1) - .FirstOr(x => x != -1, -1); - - public static T FirstOr(this IEnumerable source, T alternate) - => source.DefaultIfEmpty(alternate).First(); - - public static T FirstOr(this IEnumerable source, Func predicate, T alternate) - => source.Where(predicate).FirstOr(alternate); - - public static bool Any(this IEnumerable source) - { - foreach (var _ in source) - { - return true; - } - - return false; - } - - public static async Task> ToListAsync( - this IAsyncEnumerable source, - CancellationToken cancellationToken = default) - { - var list = new List(); - await foreach (var element in source.WithCancellation(cancellationToken)) - { - list.Add(element); - } - - return list; - } - - public static List ToList(this IEnumerable source) - => source.OfType().ToList(); - - public static string Format(this IEnumerable strings) - => "{" - + string.Join( - ", ", - strings.Select(s => "'" + s + "'")) - + "}"; -} diff --git a/src/net/KEFCore/Shared8/EnumerableMethods.cs b/src/net/KEFCore/Shared8/EnumerableMethods.cs deleted file mode 100644 index 9b49f693..00000000 --- a/src/net/KEFCore/Shared8/EnumerableMethods.cs +++ /dev/null @@ -1,615 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; - -namespace Microsoft.EntityFrameworkCore; - -internal static class EnumerableMethods -{ - //public static MethodInfo AggregateWithoutSeed { get; } - - //public static MethodInfo AggregateWithSeedWithoutSelector { get; } - - public static MethodInfo AggregateWithSeedSelector { get; } - - public static MethodInfo All { get; } - - public static MethodInfo AnyWithoutPredicate { get; } - - public static MethodInfo AnyWithPredicate { get; } - - //public static Append { get; } - - public static MethodInfo AsEnumerable { get; } - - public static MethodInfo Cast { get; } - - public static MethodInfo Concat { get; } - - public static MethodInfo Contains { get; } - - //public static MethodInfo ContainsWithComparer { get; } - - public static MethodInfo CountWithoutPredicate { get; } - - public static MethodInfo CountWithPredicate { get; } - - public static MethodInfo DefaultIfEmptyWithoutArgument { get; } - - public static MethodInfo DefaultIfEmptyWithArgument { get; } - - public static MethodInfo Distinct { get; } - - //public static MethodInfo DistinctWithComparer { get; } - - public static MethodInfo ElementAt { get; } - - public static MethodInfo ElementAtOrDefault { get; } - - //public static MethodInfo Empty { get; } - - public static MethodInfo Except { get; } - - //public static MethodInfo ExceptWithComparer { get; } - - public static MethodInfo FirstWithoutPredicate { get; } - - public static MethodInfo FirstWithPredicate { get; } - - public static MethodInfo FirstOrDefaultWithoutPredicate { get; } - - public static MethodInfo FirstOrDefaultWithPredicate { get; } - - public static MethodInfo GroupByWithKeySelector { get; } - - public static MethodInfo GroupByWithKeyElementSelector { get; } - - //public static MethodInfo GroupByWithKeySelectorAndComparer { get; } - - //public static MethodInfo GroupByWithKeyElementSelectorAndComparer { get; } - - public static MethodInfo GroupByWithKeyElementResultSelector { get; } - - public static MethodInfo GroupByWithKeyResultSelector { get; } - - //public static MethodInfo GroupByWithKeyResultSelectorAndComparer { get; } - - //public static MethodInfo GroupByWithKeyElementResultSelectorAndComparer { get; } - - public static MethodInfo GroupJoin { get; } - - //public static MethodInfo GroupJoinWithComparer { get; } - - public static MethodInfo Intersect { get; } - - //public static MethodInfo IntersectWithComparer { get; } - - public static MethodInfo Join { get; } - - public static MethodInfo JoinWithComparer { get; } - - public static MethodInfo LastWithoutPredicate { get; } - - public static MethodInfo LastWithPredicate { get; } - - public static MethodInfo LastOrDefaultWithoutPredicate { get; } - - public static MethodInfo LastOrDefaultWithPredicate { get; } - - public static MethodInfo LongCountWithoutPredicate { get; } - - public static MethodInfo LongCountWithPredicate { get; } - - public static MethodInfo MaxWithoutSelector { get; } - - public static MethodInfo MaxWithSelector { get; } - - public static MethodInfo MinWithoutSelector { get; } - - public static MethodInfo MinWithSelector { get; } - - public static MethodInfo OfType { get; } - - public static MethodInfo OrderBy { get; } - - //public static MethodInfo OrderByWithComparer { get; } - - public static MethodInfo OrderByDescending { get; } - - //public static MethodInfo OrderByDescendingWithComparer { get; } - - //public static MethodInfo Prepend { get; } - - //public static MethodInfo Range { get; } - - //public static MethodInfo Repeat { get; } - - public static MethodInfo Reverse { get; } - - public static MethodInfo Select { get; } - - public static MethodInfo SelectWithOrdinal { get; } - - public static MethodInfo SelectManyWithoutCollectionSelector { get; } - - //public static MethodInfo SelectManyWithoutCollectionSelectorOrdinal { get; } - - public static MethodInfo SelectManyWithCollectionSelector { get; } - - //public static MethodInfo SelectManyWithCollectionSelectorOrdinal { get; } - - public static MethodInfo SequenceEqual { get; } - - //public static MethodInfo SequenceEqualWithComparer { get; } - - public static MethodInfo SingleWithoutPredicate { get; } - - public static MethodInfo SingleWithPredicate { get; } - - public static MethodInfo SingleOrDefaultWithoutPredicate { get; } - - public static MethodInfo SingleOrDefaultWithPredicate { get; } - - public static MethodInfo Skip { get; } - - public static MethodInfo SkipWhile { get; } - - //public static MethodInfo SkipWhileOrdinal { get; } - - public static MethodInfo Take { get; } - - public static MethodInfo TakeWhile { get; } - - //public static MethodInfo TakeWhileOrdinal { get; } - - public static MethodInfo ThenBy { get; } - - //public static MethodInfo ThenByWithComparer { get; } - - public static MethodInfo ThenByDescending { get; } - - //public static MethodInfo ThenByDescendingWithComparer { get; } - - public static MethodInfo ToArray { get; } - - //public static MethodInfo ToDictionaryWithKeySelector { get; } - //public static MethodInfo ToDictionaryWithKeySelectorAndComparer { get; } - //public static MethodInfo ToDictionaryWithKeyElementSelector { get; } - //public static MethodInfo ToDictionaryWithKeyElementSelectorAndComparer { get; } - - //public static MethodInfo ToHashSet { get; } - //public static MethodInfo ToHashSetWithComparer { get; } - - public static MethodInfo ToList { get; } - - //public static MethodInfo ToLookupWithKeySelector { get; } - //public static MethodInfo ToLookupWithKeySelectorAndComparer { get; } - //public static MethodInfo ToLookupWithKeyElementSelector { get; } - //public static MethodInfo ToLookupWithKeyElementSelectorAndComparer { get; } - - public static MethodInfo Union { get; } - - //public static MethodInfo UnionWithComparer { get; } - - public static MethodInfo Where { get; } - - //public static MethodInfo WhereOrdinal { get; } - - public static MethodInfo ZipWithSelector { get; } - - // private static Dictionary SumWithoutSelectorMethods { get; } - private static Dictionary SumWithSelectorMethods { get; } - - // private static Dictionary AverageWithoutSelectorMethods { get; } - private static Dictionary AverageWithSelectorMethods { get; } - private static Dictionary MaxWithoutSelectorMethods { get; } - private static Dictionary MaxWithSelectorMethods { get; } - private static Dictionary MinWithoutSelectorMethods { get; } - private static Dictionary MinWithSelectorMethods { get; } - - // Not currently used - // - // public static bool IsSumWithoutSelector(MethodInfo methodInfo) - // => SumWithoutSelectorMethods.Values.Contains(methodInfo); - // - // public static bool IsSumWithSelector(MethodInfo methodInfo) - // => methodInfo.IsGenericMethod - // && SumWithSelectorMethods.Values.Contains(methodInfo.GetGenericMethodDefinition()); - // - // public static bool IsAverageWithoutSelector(MethodInfo methodInfo) - // => AverageWithoutSelectorMethods.Values.Contains(methodInfo); - // - // public static bool IsAverageWithSelector(MethodInfo methodInfo) - // => methodInfo.IsGenericMethod - // && AverageWithSelectorMethods.Values.Contains(methodInfo.GetGenericMethodDefinition()); - // - // public static MethodInfo GetSumWithoutSelector(Type type) - // => SumWithoutSelectorMethods[type]; - - public static MethodInfo GetSumWithSelector(Type type) - => SumWithSelectorMethods[type]; - - // public static MethodInfo GetAverageWithoutSelector(Type type) - // => AverageWithoutSelectorMethods[type]; - - public static MethodInfo GetAverageWithSelector(Type type) - => AverageWithSelectorMethods[type]; - - public static MethodInfo GetMaxWithoutSelector(Type type) - => MaxWithoutSelectorMethods.TryGetValue(type, out var method) - ? method - : MaxWithoutSelector; - - public static MethodInfo GetMaxWithSelector(Type type) - => MaxWithSelectorMethods.TryGetValue(type, out var method) - ? method - : MaxWithSelector; - - public static MethodInfo GetMinWithoutSelector(Type type) - => MinWithoutSelectorMethods.TryGetValue(type, out var method) - ? method - : MinWithoutSelector; - - public static MethodInfo GetMinWithSelector(Type type) - => MinWithSelectorMethods.TryGetValue(type, out var method) - ? method - : MinWithSelector; - - static EnumerableMethods() - { - var queryableMethodGroups = typeof(Enumerable) - .GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) - .GroupBy(mi => mi.Name) - .ToDictionary(e => e.Key, l => l.ToList()); - - AggregateWithSeedSelector = GetMethod( - nameof(Enumerable.Aggregate), 3, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - types[1], - typeof(Func<,,>).MakeGenericType(types[1], types[0], types[1]), - typeof(Func<,>).MakeGenericType(types[1], types[2]) - }); - - All = GetMethod( - nameof(Enumerable.All), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - AnyWithoutPredicate = GetMethod( - nameof(Enumerable.Any), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - AnyWithPredicate = GetMethod( - nameof(Enumerable.Any), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - AsEnumerable = GetMethod( - nameof(Enumerable.AsEnumerable), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Cast = GetMethod(nameof(Enumerable.Cast), 1, _ => new[] { typeof(IEnumerable) }); - - Concat = GetMethod( - nameof(Enumerable.Concat), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Contains = GetMethod( - nameof(Enumerable.Contains), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), types[0] }); - - CountWithoutPredicate = GetMethod( - nameof(Enumerable.Count), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - CountWithPredicate = GetMethod( - nameof(Enumerable.Count), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - DefaultIfEmptyWithoutArgument = GetMethod( - nameof(Enumerable.DefaultIfEmpty), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - DefaultIfEmptyWithArgument = GetMethod( - nameof(Enumerable.DefaultIfEmpty), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), types[0] }); - - Distinct = GetMethod(nameof(Enumerable.Distinct), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - ElementAt = GetMethod( - nameof(Enumerable.ElementAt), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(int) }); - - ElementAtOrDefault = GetMethod( - nameof(Enumerable.ElementAtOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(int) }); - - Except = GetMethod( - nameof(Enumerable.Except), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - FirstWithoutPredicate = GetMethod( - nameof(Enumerable.First), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - FirstWithPredicate = GetMethod( - nameof(Enumerable.First), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - FirstOrDefaultWithoutPredicate = GetMethod( - nameof(Enumerable.FirstOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - FirstOrDefaultWithPredicate = GetMethod( - nameof(Enumerable.FirstOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - GroupByWithKeySelector = GetMethod( - nameof(Enumerable.GroupBy), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - GroupByWithKeyElementSelector = GetMethod( - nameof(Enumerable.GroupBy), 3, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(Func<,>).MakeGenericType(types[0], types[1]), - typeof(Func<,>).MakeGenericType(types[0], types[2]) - }); - - GroupByWithKeyElementResultSelector = GetMethod( - nameof(Enumerable.GroupBy), 4, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(Func<,>).MakeGenericType(types[0], types[1]), - typeof(Func<,>).MakeGenericType(types[0], types[2]), - typeof(Func<,,>).MakeGenericType( - types[1], typeof(IEnumerable<>).MakeGenericType(types[2]), types[3]) - }); - - GroupByWithKeyResultSelector = GetMethod( - nameof(Enumerable.GroupBy), 3, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(Func<,>).MakeGenericType(types[0], types[1]), - typeof(Func<,,>).MakeGenericType( - types[1], typeof(IEnumerable<>).MakeGenericType(types[0]), types[2]) - }); - - GroupJoin = GetMethod( - nameof(Enumerable.GroupJoin), 4, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(IEnumerable<>).MakeGenericType(types[1]), - typeof(Func<,>).MakeGenericType(types[0], types[2]), - typeof(Func<,>).MakeGenericType(types[1], types[2]), - typeof(Func<,,>).MakeGenericType( - types[0], typeof(IEnumerable<>).MakeGenericType(types[1]), types[3]) - }); - - Intersect = GetMethod( - nameof(Enumerable.Intersect), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Join = GetMethod( - nameof(Enumerable.Join), 4, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(IEnumerable<>).MakeGenericType(types[1]), - typeof(Func<,>).MakeGenericType(types[0], types[2]), - typeof(Func<,>).MakeGenericType(types[1], types[2]), - typeof(Func<,,>).MakeGenericType(types[0], types[1], types[3]) - }); - - JoinWithComparer = GetMethod( - nameof(Enumerable.Join), 4, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(IEnumerable<>).MakeGenericType(types[1]), - typeof(Func<,>).MakeGenericType(types[0], types[2]), - typeof(Func<,>).MakeGenericType(types[1], types[2]), - typeof(Func<,,>).MakeGenericType(types[0], types[1], types[3]), - typeof(IEqualityComparer<>).MakeGenericType(types[2]) - }); - - LastWithoutPredicate = GetMethod( - nameof(Enumerable.Last), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - LastWithPredicate = GetMethod( - nameof(Enumerable.Last), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - LastOrDefaultWithoutPredicate = GetMethod( - nameof(Enumerable.LastOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - LastOrDefaultWithPredicate = GetMethod( - nameof(Enumerable.LastOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - LongCountWithoutPredicate = GetMethod( - nameof(Enumerable.LongCount), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - LongCountWithPredicate = GetMethod( - nameof(Enumerable.LongCount), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - MaxWithoutSelector = GetMethod(nameof(Enumerable.Max), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - MaxWithSelector = GetMethod( - nameof(Enumerable.Max), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - MinWithoutSelector = GetMethod(nameof(Enumerable.Min), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - MinWithSelector = GetMethod( - nameof(Enumerable.Min), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - OfType = GetMethod(nameof(Enumerable.OfType), 1, _ => new[] { typeof(IEnumerable) }); - - OrderBy = GetMethod( - nameof(Enumerable.OrderBy), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - OrderByDescending = GetMethod( - nameof(Enumerable.OrderByDescending), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - Reverse = GetMethod(nameof(Enumerable.Reverse), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Select = GetMethod( - nameof(Enumerable.Select), 2, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - SelectWithOrdinal = GetMethod( - nameof(Enumerable.Select), 2, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,,>).MakeGenericType(types[0], typeof(int), types[1]) - }); - - SelectManyWithoutCollectionSelector = GetMethod( - nameof(Enumerable.SelectMany), 2, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(Func<,>).MakeGenericType( - types[0], typeof(IEnumerable<>).MakeGenericType(types[1])) - }); - - SelectManyWithCollectionSelector = GetMethod( - nameof(Enumerable.SelectMany), 3, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(Func<,>).MakeGenericType( - types[0], typeof(IEnumerable<>).MakeGenericType(types[1])), - typeof(Func<,,>).MakeGenericType(types[0], types[1], types[2]) - }); - - SequenceEqual = GetMethod( - nameof(Enumerable.SequenceEqual), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - SingleWithoutPredicate = GetMethod( - nameof(Enumerable.Single), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - SingleWithPredicate = GetMethod( - nameof(Enumerable.Single), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - SingleOrDefaultWithoutPredicate = GetMethod( - nameof(Enumerable.SingleOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - SingleOrDefaultWithPredicate = GetMethod( - nameof(Enumerable.SingleOrDefault), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - Skip = GetMethod( - nameof(Enumerable.Skip), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(int) }); - - SkipWhile = GetMethod( - nameof(Enumerable.SkipWhile), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - ToArray = GetMethod(nameof(Enumerable.ToArray), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - ToList = GetMethod(nameof(Enumerable.ToList), 1, types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Take = GetMethod( - nameof(Enumerable.Take), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(int) }); - - TakeWhile = GetMethod( - nameof(Enumerable.TakeWhile), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - ThenBy = GetMethod( - nameof(Enumerable.ThenBy), 2, - types => new[] { typeof(IOrderedEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - ThenByDescending = GetMethod( - nameof(Enumerable.ThenByDescending), 2, - types => new[] { typeof(IOrderedEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], types[1]) }); - - Union = GetMethod( - nameof(Enumerable.Union), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(IEnumerable<>).MakeGenericType(types[0]) }); - - Where = GetMethod( - nameof(Enumerable.Where), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], typeof(bool)) }); - - ZipWithSelector = GetMethod( - nameof(Enumerable.Zip), 3, - types => new[] - { - typeof(IEnumerable<>).MakeGenericType(types[0]), - typeof(IEnumerable<>).MakeGenericType(types[1]), - typeof(Func<,,>).MakeGenericType(types[0], types[1], types[2]) - }); - - var numericTypes = new[] - { - typeof(int), - typeof(int?), - typeof(long), - typeof(long?), - typeof(float), - typeof(float?), - typeof(double), - typeof(double?), - typeof(decimal), - typeof(decimal?) - }; - - // AverageWithoutSelectorMethods = new Dictionary(); - AverageWithSelectorMethods = new Dictionary(); - MaxWithoutSelectorMethods = new Dictionary(); - MaxWithSelectorMethods = new Dictionary(); - MinWithoutSelectorMethods = new Dictionary(); - MinWithSelectorMethods = new Dictionary(); - // SumWithoutSelectorMethods = new Dictionary(); - SumWithSelectorMethods = new Dictionary(); - - foreach (var type in numericTypes) - { - // AverageWithoutSelectorMethods[type] = GetMethod( - // nameof(Enumerable.Average), 0, types => new[] { typeof(IEnumerable<>).MakeGenericType(type) }); - AverageWithSelectorMethods[type] = GetMethod( - nameof(Enumerable.Average), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], type) }); - MaxWithoutSelectorMethods[type] = GetMethod( - nameof(Enumerable.Max), 0, _ => new[] { typeof(IEnumerable<>).MakeGenericType(type) }); - MaxWithSelectorMethods[type] = GetMethod( - nameof(Enumerable.Max), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], type) }); - MinWithoutSelectorMethods[type] = GetMethod( - nameof(Enumerable.Min), 0, _ => new[] { typeof(IEnumerable<>).MakeGenericType(type) }); - MinWithSelectorMethods[type] = GetMethod( - nameof(Enumerable.Min), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], type) }); - // SumWithoutSelectorMethods[type] = GetMethod( - // nameof(Enumerable.Sum), 0, types => new[] { typeof(IEnumerable<>).MakeGenericType(type) }); - SumWithSelectorMethods[type] = GetMethod( - nameof(Enumerable.Sum), 1, - types => new[] { typeof(IEnumerable<>).MakeGenericType(types[0]), typeof(Func<,>).MakeGenericType(types[0], type) }); - } - - MethodInfo GetMethod(string name, int genericParameterCount, Func parameterGenerator) - => queryableMethodGroups[name].Single( - mi => ((genericParameterCount == 0 && !mi.IsGenericMethod) - || (mi.IsGenericMethod && mi.GetGenericArguments().Length == genericParameterCount)) - && mi.GetParameters().Select(e => e.ParameterType).SequenceEqual( - parameterGenerator(mi.IsGenericMethod ? mi.GetGenericArguments() : Array.Empty()))); - } -} diff --git a/src/net/KEFCore/Shared8/ExpressionExtensions.cs b/src/net/KEFCore/Shared8/ExpressionExtensions.cs deleted file mode 100644 index 0835bd7a..00000000 --- a/src/net/KEFCore/Shared8/ExpressionExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Diagnostics.CodeAnalysis; - -// ReSharper disable once CheckNamespace -namespace System.Linq.Expressions; - -[DebuggerStepThrough] -internal static class ExpressionExtensions -{ - public static bool IsNullConstantExpression(this Expression expression) - => RemoveConvert(expression) is ConstantExpression { Value: null }; - - public static LambdaExpression UnwrapLambdaFromQuote(this Expression expression) - => (LambdaExpression)(expression is UnaryExpression unary && expression.NodeType == ExpressionType.Quote - ? unary.Operand - : expression); - - [return: NotNullIfNotNull("expression")] - public static Expression? UnwrapTypeConversion(this Expression? expression, out Type? convertedType) - { - convertedType = null; - while (expression is UnaryExpression - { - NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs - } unaryExpression) - { - expression = unaryExpression.Operand; - if (unaryExpression.Type != typeof(object) // Ignore object conversion - && !unaryExpression.Type.IsAssignableFrom(expression.Type)) // Ignore casting to base type/interface - { - convertedType = unaryExpression.Type; - } - } - - return expression; - } - - private static Expression RemoveConvert(Expression expression) - => expression is UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } unaryExpression - ? RemoveConvert(unaryExpression.Operand) - : expression; - - public static T GetConstantValue(this Expression expression) - => expression is ConstantExpression constantExpression - ? (T)constantExpression.Value! - : throw new InvalidOperationException(); -} diff --git a/src/net/KEFCore/Shared8/Graph.cs b/src/net/KEFCore/Shared8/Graph.cs deleted file mode 100644 index 0fc0d1ae..00000000 --- a/src/net/KEFCore/Shared8/Graph.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.Utilities; - -internal abstract class Graph -{ - public abstract IEnumerable Vertices { get; } - - public abstract void Clear(); - - public abstract IEnumerable GetOutgoingNeighbors(TVertex from); - - public abstract IEnumerable GetIncomingNeighbors(TVertex to); - - public ISet GetUnreachableVertices(IReadOnlyList roots) - { - var unreachableVertices = new HashSet(Vertices); - unreachableVertices.ExceptWith(roots); - var visitingQueue = new List(roots); - - var currentVertexIndex = 0; - while (currentVertexIndex < visitingQueue.Count) - { - var currentVertex = visitingQueue[currentVertexIndex]; - currentVertexIndex++; - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var neighbor in GetOutgoingNeighbors(currentVertex)) - { - if (unreachableVertices.Remove(neighbor)) - { - visitingQueue.Add(neighbor); - } - } - } - - return unreachableVertices; - } -} diff --git a/src/net/KEFCore/Shared8/MemberInfoExtensions.cs b/src/net/KEFCore/Shared8/MemberInfoExtensions.cs deleted file mode 100644 index 06564f5c..00000000 --- a/src/net/KEFCore/Shared8/MemberInfoExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -namespace System.Reflection; - -internal static class EntityFrameworkMemberInfoExtensions -{ - public static Type GetMemberType(this MemberInfo memberInfo) - => (memberInfo as PropertyInfo)?.PropertyType ?? ((FieldInfo)memberInfo).FieldType; - - public static bool IsSameAs(this MemberInfo? propertyInfo, MemberInfo? otherPropertyInfo) - => propertyInfo == null - ? otherPropertyInfo == null - : (otherPropertyInfo != null - && (Equals(propertyInfo, otherPropertyInfo) - || (propertyInfo.Name == otherPropertyInfo.Name - && propertyInfo.DeclaringType != null - && otherPropertyInfo.DeclaringType != null - && (propertyInfo.DeclaringType == otherPropertyInfo.DeclaringType - || propertyInfo.DeclaringType.GetTypeInfo().IsSubclassOf(otherPropertyInfo.DeclaringType) - || otherPropertyInfo.DeclaringType.GetTypeInfo().IsSubclassOf(propertyInfo.DeclaringType) - || propertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces.Contains(otherPropertyInfo.DeclaringType) - || otherPropertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces - .Contains(propertyInfo.DeclaringType))))); - - public static bool IsOverriddenBy(this MemberInfo? propertyInfo, MemberInfo? otherPropertyInfo) - => propertyInfo == null - ? otherPropertyInfo == null - : (otherPropertyInfo != null - && (Equals(propertyInfo, otherPropertyInfo) - || (propertyInfo.Name == otherPropertyInfo.Name - && propertyInfo.DeclaringType != null - && otherPropertyInfo.DeclaringType != null - && (propertyInfo.DeclaringType == otherPropertyInfo.DeclaringType - || otherPropertyInfo.DeclaringType.GetTypeInfo().IsSubclassOf(propertyInfo.DeclaringType) - || otherPropertyInfo.DeclaringType.GetTypeInfo().ImplementedInterfaces - .Contains(propertyInfo.DeclaringType))))); - - public static string GetSimpleMemberName(this MemberInfo member) - { - var name = member.Name; - var index = name.LastIndexOf('.'); - return index >= 0 ? name[(index + 1)..] : name; - } - - public static bool IsReallyVirtual(this MethodInfo method) - => method is { IsVirtual: true, IsFinal: false }; -} diff --git a/src/net/KEFCore/Shared8/MethodInfoExtensions.cs b/src/net/KEFCore/Shared8/MethodInfoExtensions.cs deleted file mode 100644 index 1d86c668..00000000 --- a/src/net/KEFCore/Shared8/MethodInfoExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Collections.Immutable; - -namespace System.Reflection; - -internal static class MethodInfoExtensions -{ - public static bool IsContainsMethod(this MethodInfo method) - => method is { Name: nameof(IList.Contains), DeclaringType: not null } - && method.DeclaringType.GetInterfaces().Append(method.DeclaringType).Any( - t => t == typeof(IList) - || (t.IsGenericType - && t.GetGenericTypeDefinition() is Type genericType - && (genericType == typeof(ICollection<>) - || genericType == typeof(IReadOnlySet<>) - || genericType == typeof(IImmutableSet<>)))); -} diff --git a/src/net/KEFCore/Shared8/Multigraph.cs b/src/net/KEFCore/Shared8/Multigraph.cs deleted file mode 100644 index ff803950..00000000 --- a/src/net/KEFCore/Shared8/Multigraph.cs +++ /dev/null @@ -1,386 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -namespace Microsoft.EntityFrameworkCore.Utilities; - -internal class Multigraph : Graph - where TVertex : notnull -{ - private readonly IComparer? _secondarySortComparer; - private readonly HashSet _vertices = new(); - private readonly Dictionary> _successorMap = new(); - private readonly Dictionary> _predecessorMap = new(); - - public Multigraph() - { - } - - public Multigraph(IComparer secondarySortComparer) - { - _secondarySortComparer = secondarySortComparer; - } - - public Multigraph(Comparison secondarySortComparer) - : this(Comparer.Create(secondarySortComparer)) - { - } - - public IEnumerable GetEdges(TVertex from, TVertex to) - { - if (_successorMap.TryGetValue(from, out var successorSet)) - { - if (successorSet.TryGetValue(to, out var edges)) - { - return edges is IEnumerable edgeList ? edgeList.Select(e => e.Payload) : (new[] { ((Edge)edges!).Payload }); - } - } - - return Enumerable.Empty(); - } - - public void AddVertex(TVertex vertex) - => _vertices.Add(vertex); - - public void AddVertices(IEnumerable vertices) - => _vertices.UnionWith(vertices); - - public void AddEdge(TVertex from, TVertex to, TEdge payload, bool requiresBatchingBoundary = false) - { -#if DEBUG - if (!_vertices.Contains(from)) - { - throw new InvalidOperationException(CoreStrings.GraphDoesNotContainVertex(from)); - } - - if (!_vertices.Contains(to)) - { - throw new InvalidOperationException(CoreStrings.GraphDoesNotContainVertex(to)); - } -#endif - - var edge = new Edge(payload, requiresBatchingBoundary); - - if (!_successorMap.TryGetValue(from, out var successorEdges)) - { - successorEdges = new Dictionary(); - _successorMap.Add(from, successorEdges); - } - - if (successorEdges.TryGetValue(to, out var edges)) - { - if (edges is not List edgeList) - { - edgeList = new List { (Edge)edges! }; - successorEdges[to] = edgeList; - } - - edgeList.Add(edge); - } - else - { - successorEdges.Add(to, edge); - } - - if (!_predecessorMap.TryGetValue(to, out var predecessorEdges)) - { - predecessorEdges = new Dictionary(); - _predecessorMap.Add(to, predecessorEdges); - } - - if (predecessorEdges.TryGetValue(from, out edges)) - { - if (edges is not List edgeList) - { - edgeList = new List { (Edge)edges! }; - predecessorEdges[from] = edgeList; - } - - edgeList.Add(edge); - } - else - { - predecessorEdges.Add(from, edge); - } - } - - public override void Clear() - { - _vertices.Clear(); - _successorMap.Clear(); - _predecessorMap.Clear(); - } - - public IReadOnlyList TopologicalSort() - => TopologicalSort(null, null); - - public IReadOnlyList TopologicalSort( - Func, bool> tryBreakEdge) - => TopologicalSort(tryBreakEdge, null); - - public IReadOnlyList TopologicalSort( - Func>>, string> formatCycle) - => TopologicalSort(null, formatCycle); - - public IReadOnlyList TopologicalSort( - Func, bool>? tryBreakEdge, - Func>>, string>? formatCycle, - Func? formatException = null) - { - var batches = TopologicalSortCore(withBatching: false, tryBreakEdge, formatCycle, formatException); - - Check.DebugAssert(batches.Count < 2, "TopologicalSortCore did batching but withBatching was false"); - - return batches.Count == 1 - ? batches[0] - : Array.Empty(); - } - - protected virtual string? ToString(TVertex vertex) - => vertex.ToString(); - - public IReadOnlyList> BatchingTopologicalSort() - => BatchingTopologicalSort(null, null); - - public IReadOnlyList> BatchingTopologicalSort( - Func, bool>? canBreakEdges, - Func>>, string>? formatCycle, - Func? formatException = null) - => TopologicalSortCore(withBatching: true, canBreakEdges, formatCycle, formatException); - - private IReadOnlyList> TopologicalSortCore( - bool withBatching, - Func, bool>? canBreakEdges, - Func>>, string>? formatCycle, - Func? formatException = null) - { - // Performs a breadth-first topological sort (Kahn's algorithm) - var result = new List>(); - var currentRootsQueue = new List(); - var nextRootsQueue = new List(); - var vertexesProcessed = 0; - var batchBoundaryRequired = false; - var currentBatch = new List(); - var currentBatchSet = new HashSet(); - - var predecessorCounts = new Dictionary(_predecessorMap.Count); - foreach (var (vertex, vertices) in _predecessorMap) - { - predecessorCounts[vertex] = vertices.Count; - } - - // Bootstrap the topological sort by finding all vertexes which have no predecessors - foreach (var vertex in _vertices) - { - if (!predecessorCounts.ContainsKey(vertex)) - { - currentRootsQueue.Add(vertex); - } - } - - result.Add(currentBatch); - - while (vertexesProcessed < _vertices.Count) - { - while (currentRootsQueue.Count > 0) - { - // Secondary sorting: after the first topological sorting (according to dependencies between the commands as expressed in - // the graph), we apply an optional secondary sort. - // When sorting modification commands, this ensures a deterministic ordering and prevents deadlocks between concurrent - // transactions locking the same rows in different orders. - if (_secondarySortComparer is not null) - { - currentRootsQueue.Sort(_secondarySortComparer); - } - - // If we detected in the last roots pass that a batch boundary is required, close the current batch and start a new one. - if (batchBoundaryRequired) - { - currentBatch = new List(); - result.Add(currentBatch); - currentBatchSet.Clear(); - - batchBoundaryRequired = false; - } - - foreach (var currentRoot in currentRootsQueue) - { - currentBatch.Add(currentRoot); - currentBatchSet.Add(currentRoot); - vertexesProcessed++; - - foreach (var successor in GetOutgoingNeighbors(currentRoot)) - { - predecessorCounts[successor]--; - - // If the successor has no other predecessors, add it for processing in the next roots pass. - if (predecessorCounts[successor] == 0) - { - nextRootsQueue.Add(successor); - CheckBatchingBoundary(successor); - } - } - } - - // Finished passing over the current roots, move on to the next set. - (currentRootsQueue, nextRootsQueue) = (nextRootsQueue, currentRootsQueue); - nextRootsQueue.Clear(); - } - - // We have no more roots to process. That either means we're done, or that there's a cycle which we need to break - if (vertexesProcessed < _vertices.Count) - { - var broken = false; - - var candidateVertices = predecessorCounts.Keys.ToList(); - var candidateIndex = 0; - - while ((candidateIndex < candidateVertices.Count) - && !broken - && canBreakEdges != null) - { - var candidateVertex = candidateVertices[candidateIndex]; - if (predecessorCounts[candidateVertex] == 0) - { - candidateIndex++; - continue; - } - - // Find a vertex in the unsorted portion of the graph that has edges to the candidate - var incomingNeighbor = GetIncomingNeighbors(candidateVertex) - .First( - neighbor => predecessorCounts.TryGetValue(neighbor, out var neighborPredecessors) - && neighborPredecessors > 0); - - if (canBreakEdges(incomingNeighbor, candidateVertex, GetEdges(incomingNeighbor, candidateVertex))) - { - var removed = _successorMap[incomingNeighbor].Remove(candidateVertex); - Check.DebugAssert(removed, "Candidate vertex not found in successor map"); - removed = _predecessorMap[candidateVertex].Remove(incomingNeighbor); - Check.DebugAssert(removed, "Incoming neighbor not found in predecessor map"); - - predecessorCounts[candidateVertex]--; - if (predecessorCounts[candidateVertex] == 0) - { - currentRootsQueue.Add(candidateVertex); - CheckBatchingBoundary(candidateVertex); - broken = true; - } - - continue; - } - - candidateIndex++; - } - - if (broken) - { - continue; - } - - var currentCycleVertex = _vertices.First( - v => predecessorCounts.TryGetValue(v, out var predecessorCount) && predecessorCount != 0); - var cycle = new List { currentCycleVertex }; - var finished = false; - while (!finished) - { - foreach (var predecessor in GetIncomingNeighbors(currentCycleVertex)) - { - if (!predecessorCounts.TryGetValue(predecessor, out var predecessorCount) - || predecessorCount == 0) - { - continue; - } - - predecessorCounts[currentCycleVertex] = -1; - - currentCycleVertex = predecessor; - cycle.Add(currentCycleVertex); - finished = predecessorCounts[predecessor] == -1; - break; - } - } - - cycle.Reverse(); - - // Remove any tail that's not part of the cycle - var startingVertex = cycle[0]; - for (var i = cycle.Count - 1; i >= 0; i--) - { - if (cycle[i].Equals(startingVertex)) - { - break; - } - - cycle.RemoveAt(i); - } - - ThrowCycle(cycle, formatCycle, formatException); - } - } - - return result; - - // Detect batch boundary (if batching is enabled). - // If the successor has any predecessor where the edge requires a batching boundary, and that predecessor is - // already in the current batch, then the next batch will have to be executed in a separate batch. - // TODO: Optimization: Instead of currentBatchSet, store a batch counter on each vertex, and check if later - // vertexes have a boundary-requiring dependency on a vertex with the same batch counter. - void CheckBatchingBoundary(TVertex vertex) - { - if (withBatching - && _predecessorMap[vertex].Any( - kv => - (kv.Value is Edge { RequiresBatchingBoundary: true } - || kv.Value is IEnumerable edges && edges.Any(e => e.RequiresBatchingBoundary)) - && currentBatchSet.Contains(kv.Key))) - { - batchBoundaryRequired = true; - } - } - } - - private void ThrowCycle( - List cycle, - Func>>, string>? formatCycle, - Func? formatException = null) - { - string cycleString; - if (formatCycle == null) - { - cycleString = cycle.Select(e => ToString(e)!).Join(" ->" + Environment.NewLine); - } - else - { - var currentCycleVertex = cycle.First(); - var cycleData = new List>>(); - - foreach (var vertex in cycle.Skip(1)) - { - cycleData.Add(Tuple.Create(currentCycleVertex, vertex, GetEdges(currentCycleVertex, vertex))); - currentCycleVertex = vertex; - } - - cycleString = formatCycle(cycleData); - } - - var message = formatException == null ? CoreStrings.CircularDependency(cycleString) : formatException(cycleString); - throw new InvalidOperationException(message); - } - - public override IEnumerable Vertices - => _vertices; - - public override IEnumerable GetOutgoingNeighbors(TVertex from) - => _successorMap.TryGetValue(from, out var successorSet) - ? successorSet.Keys - : Enumerable.Empty(); - - public override IEnumerable GetIncomingNeighbors(TVertex to) - => _predecessorMap.TryGetValue(to, out var predecessors) - ? predecessors.Keys - : Enumerable.Empty(); - - private record struct Edge(TEdge Payload, bool RequiresBatchingBoundary); -} diff --git a/src/net/KEFCore/Shared8/NonCapturingLazyInitializer.cs b/src/net/KEFCore/Shared8/NonCapturingLazyInitializer.cs deleted file mode 100644 index 42412bda..00000000 --- a/src/net/KEFCore/Shared8/NonCapturingLazyInitializer.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Diagnostics.CodeAnalysis; -using Microsoft.EntityFrameworkCore.Utilities; - -namespace Microsoft.EntityFrameworkCore.Internal; - -internal static class NonCapturingLazyInitializer -{ - public static TValue EnsureInitialized( - [NotNull] ref TValue? target, - TParam param, - Func valueFactory) - where TValue : class - { - var tmp = Volatile.Read(ref target); - if (tmp != null) - { - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - return tmp; - } - - Interlocked.CompareExchange(ref target, valueFactory(param), null); - - return target; - } - - public static TValue EnsureInitialized( - [NotNull] ref TValue? target, - TParam1 param1, - TParam2 param2, - Func valueFactory) - where TValue : class - { - var tmp = Volatile.Read(ref target); - if (tmp != null) - { - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - return tmp; - } - - Interlocked.CompareExchange(ref target, valueFactory(param1, param2), null); - - return target; - } - - public static TValue EnsureInitialized( - [NotNull] ref TValue? target, - TParam1 param1, - TParam2 param2, - TParam3 param3, - Func valueFactory) - where TValue : class - { - var tmp = Volatile.Read(ref target); - if (tmp != null) - { - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - return tmp; - } - - Interlocked.CompareExchange(ref target, valueFactory(param1, param2, param3), null); - - return target; - } - - public static TValue EnsureInitialized( - ref TValue target, - ref bool initialized, - TParam param, - Func valueFactory) - where TValue : class? - { - var alreadyInitialized = Volatile.Read(ref initialized); - if (alreadyInitialized) - { - var value = Volatile.Read(ref target); - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - Check.DebugAssert(value != null, $"value was null in {nameof(EnsureInitialized)} after check"); - return value; - } - - Volatile.Write(ref target, valueFactory(param)); - Volatile.Write(ref initialized, true); - - return target; - } - - public static TValue EnsureInitialized( - [NotNull] ref TValue? target, - TValue value) - where TValue : class - { - var tmp = Volatile.Read(ref target); - if (tmp != null) - { - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - return tmp; - } - - Interlocked.CompareExchange(ref target, value, null); - - return target; - } - - public static TValue EnsureInitialized( - [NotNull] ref TValue? target, - TParam param, - Action valueFactory) - where TValue : class - { - var tmp = Volatile.Read(ref target); - if (tmp != null) - { - Check.DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check"); - return tmp; - } - - valueFactory(param); - - var tmp2 = Volatile.Read(ref target); - Check.DebugAssert( - target != null && tmp2 != null, - $"{nameof(valueFactory)} did not initialize {nameof(target)} in {nameof(EnsureInitialized)}"); - return tmp2; - } -} diff --git a/src/net/KEFCore/Shared8/PropertyInfoExtensions.cs b/src/net/KEFCore/Shared8/PropertyInfoExtensions.cs deleted file mode 100644 index e93d8c47..00000000 --- a/src/net/KEFCore/Shared8/PropertyInfoExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -// ReSharper disable once CheckNamespace - -namespace System.Reflection; - -[DebuggerStepThrough] -internal static class PropertyInfoExtensions -{ - public static bool IsStatic(this PropertyInfo property) - => (property.GetMethod ?? property.SetMethod)!.IsStatic; - - public static bool IsCandidateProperty(this MemberInfo memberInfo, bool needsWrite = true, bool publicOnly = true) - => memberInfo is PropertyInfo propertyInfo - ? !propertyInfo.IsStatic() - && propertyInfo.CanRead - && (!needsWrite || propertyInfo.FindSetterProperty() != null) - && propertyInfo.GetMethod != null - && (!publicOnly || propertyInfo.GetMethod.IsPublic) - && propertyInfo.GetIndexParameters().Length == 0 - : memberInfo is FieldInfo { IsStatic: false } fieldInfo - && (!publicOnly || fieldInfo.IsPublic); - - public static bool IsIndexerProperty(this PropertyInfo propertyInfo) - { - var indexParams = propertyInfo.GetIndexParameters(); - return indexParams.Length == 1 - && indexParams[0].ParameterType == typeof(string); - } - - public static PropertyInfo? FindGetterProperty(this PropertyInfo propertyInfo) - => propertyInfo.DeclaringType! - .GetPropertiesInHierarchy(propertyInfo.GetSimpleMemberName()) - .FirstOrDefault(p => p.GetMethod != null); - - public static PropertyInfo? FindSetterProperty(this PropertyInfo propertyInfo) - => propertyInfo.DeclaringType! - .GetPropertiesInHierarchy(propertyInfo.GetSimpleMemberName()) - .FirstOrDefault(p => p.SetMethod != null); -} diff --git a/src/net/KEFCore/Shared8/SharedTypeExtensions.cs b/src/net/KEFCore/Shared8/SharedTypeExtensions.cs deleted file mode 100644 index 4a116ef6..00000000 --- a/src/net/KEFCore/Shared8/SharedTypeExtensions.cs +++ /dev/null @@ -1,619 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Text; - -// ReSharper disable once CheckNamespace -namespace System; - -[DebuggerStepThrough] -internal static class SharedTypeExtensions -{ - private static readonly Dictionary BuiltInTypeNames = new() - { - { typeof(bool), "bool" }, - { typeof(byte), "byte" }, - { typeof(char), "char" }, - { typeof(decimal), "decimal" }, - { typeof(double), "double" }, - { typeof(float), "float" }, - { typeof(int), "int" }, - { typeof(long), "long" }, - { typeof(object), "object" }, - { typeof(sbyte), "sbyte" }, - { typeof(short), "short" }, - { typeof(string), "string" }, - { typeof(uint), "uint" }, - { typeof(ulong), "ulong" }, - { typeof(ushort), "ushort" }, - { typeof(void), "void" } - }; - - public static Type UnwrapNullableType(this Type type) - => Nullable.GetUnderlyingType(type) ?? type; - - public static bool IsNullableValueType(this Type type) - => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - - public static bool IsNullableType(this Type type) - => !type.IsValueType || type.IsNullableValueType(); - - public static bool IsValidEntityType(this Type type) - => type is { IsClass: true, IsArray: false } - && type != typeof(string); - - public static bool IsValidComplexType(this Type type) - => !type.IsArray - && !type.IsInterface - && !IsScalarType(type); - - public static bool IsScalarType(this Type type) - => type == typeof(string) - || CommonTypeDictionary.ContainsKey(type); - - public static bool IsPropertyBagType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) - { - if (type.IsGenericTypeDefinition) - { - return false; - } - - var types = GetGenericTypeImplementations(type, typeof(IDictionary<,>)); - return types.Any( - t => t.GetGenericArguments()[0] == typeof(string) - && t.GetGenericArguments()[1] == typeof(object)); - } - - public static Type MakeNullable(this Type type, bool nullable = true) - => type.IsNullableType() == nullable - ? type - : nullable - ? typeof(Nullable<>).MakeGenericType(type) - : type.UnwrapNullableType(); - - public static bool IsNumeric(this Type type) - { - type = type.UnwrapNullableType(); - - return type.IsInteger() - || type == typeof(decimal) - || type == typeof(float) - || type == typeof(double); - } - - public static bool IsInteger(this Type type) - { - type = type.UnwrapNullableType(); - - return type == typeof(int) - || type == typeof(long) - || type == typeof(short) - || type == typeof(byte) - || type == typeof(uint) - || type == typeof(ulong) - || type == typeof(ushort) - || type == typeof(sbyte) - || type == typeof(char); - } - - public static bool IsSignedInteger(this Type type) - => type == typeof(int) - || type == typeof(long) - || type == typeof(short) - || type == typeof(sbyte); - - public static bool IsAnonymousType(this Type type) - => type.Name.StartsWith("<>", StringComparison.Ordinal) - && type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Length > 0 - && type.Name.Contains("AnonymousType"); - - public static PropertyInfo? GetAnyProperty( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] - this Type type, - string name) - { - var props = type.GetRuntimeProperties().Where(p => p.Name == name).ToList(); - if (props.Count > 1) - { - throw new AmbiguousMatchException(); - } - - return props.SingleOrDefault(); - } - - public static bool IsInstantiable(this Type type) - => type is { IsAbstract: false, IsInterface: false } - && (!type.IsGenericType || !type.IsGenericTypeDefinition); - - public static Type UnwrapEnumType(this Type type) - { - var isNullable = type.IsNullableType(); - var underlyingNonNullableType = isNullable ? type.UnwrapNullableType() : type; - if (!underlyingNonNullableType.IsEnum) - { - return type; - } - - var underlyingEnumType = Enum.GetUnderlyingType(underlyingNonNullableType); - return isNullable ? MakeNullable(underlyingEnumType) : underlyingEnumType; - } - - public static Type GetSequenceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) - { - var sequenceType = TryGetSequenceType(type); - if (sequenceType == null) - { - throw new ArgumentException($"The type {type.Name} does not represent a sequence"); - } - - return sequenceType; - } - - public static Type? TryGetSequenceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) - => type.TryGetElementType(typeof(IEnumerable<>)) - ?? type.TryGetElementType(typeof(IAsyncEnumerable<>)); - - public static Type? TryGetElementType( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type, - Type interfaceOrBaseType) - { - if (type.IsGenericTypeDefinition) - { - return null; - } - - var types = GetGenericTypeImplementations(type, interfaceOrBaseType); - - Type? singleImplementation = null; - foreach (var implementation in types) - { - if (singleImplementation == null) - { - singleImplementation = implementation; - } - else - { - singleImplementation = null; - break; - } - } - - return singleImplementation?.GenericTypeArguments.FirstOrDefault(); - } - - public static bool IsCompatibleWith(this Type propertyType, Type fieldType) - { - if (propertyType.IsAssignableFrom(fieldType) - || fieldType.IsAssignableFrom(propertyType)) - { - return true; - } - - var propertyElementType = propertyType.TryGetSequenceType(); - var fieldElementType = fieldType.TryGetSequenceType(); - - return propertyElementType != null - && fieldElementType != null - && IsCompatibleWith(propertyElementType, fieldElementType); - } - - public static IEnumerable GetGenericTypeImplementations(this Type type, Type interfaceOrBaseType) - { - var typeInfo = type.GetTypeInfo(); - if (!typeInfo.IsGenericTypeDefinition) - { - var baseTypes = interfaceOrBaseType.GetTypeInfo().IsInterface - ? typeInfo.ImplementedInterfaces - : type.GetBaseTypes(); - foreach (var baseType in baseTypes) - { - if (baseType.IsGenericType - && baseType.GetGenericTypeDefinition() == interfaceOrBaseType) - { - yield return baseType; - } - } - - if (type.IsGenericType - && type.GetGenericTypeDefinition() == interfaceOrBaseType) - { - yield return type; - } - } - } - - public static IEnumerable GetBaseTypes(this Type type) - { - var currentType = type.BaseType; - - while (currentType != null) - { - yield return currentType; - - currentType = currentType.BaseType; - } - } - - public static List GetBaseTypesAndInterfacesInclusive(this Type type) - { - var baseTypes = new List(); - var typesToProcess = new Queue(); - typesToProcess.Enqueue(type); - - while (typesToProcess.Count > 0) - { - type = typesToProcess.Dequeue(); - baseTypes.Add(type); - - if (type.IsNullableValueType()) - { - typesToProcess.Enqueue(Nullable.GetUnderlyingType(type)!); - } - - if (type.IsConstructedGenericType) - { - typesToProcess.Enqueue(type.GetGenericTypeDefinition()); - } - - if (type is { IsGenericTypeDefinition: false, IsInterface: false }) - { - if (type.BaseType != null) - { - typesToProcess.Enqueue(type.BaseType); - } - - foreach (var @interface in GetDeclaredInterfaces(type)) - { - typesToProcess.Enqueue(@interface); - } - } - } - - return baseTypes; - } - - public static IEnumerable GetTypesInHierarchy(this Type type) - { - var currentType = type; - - while (currentType != null) - { - yield return currentType; - - currentType = currentType.BaseType; - } - } - - public static IEnumerable GetDeclaredInterfaces( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) - { - var interfaces = type.GetInterfaces(); - if (type.BaseType == typeof(object) - || type.BaseType == null) - { - return interfaces; - } - - return interfaces.Except(GetInterfacesSuppressed(type.BaseType)); - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070", Justification = "https://github.com/dotnet/linker/issues/2473")] - static IEnumerable GetInterfacesSuppressed(Type type) - => type.GetInterfaces(); - } - - public static ConstructorInfo? GetDeclaredConstructor( - [DynamicallyAccessedMembers( - DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - this Type type, - Type[]? types) - { - types ??= Array.Empty(); - - return type.GetTypeInfo().DeclaredConstructors - .SingleOrDefault( - c => !c.IsStatic - && c.GetParameters().Select(p => p.ParameterType).SequenceEqual(types))!; - } - - public static IEnumerable GetPropertiesInHierarchy(this Type type, string name) - { - var currentType = type; - do - { - var typeInfo = currentType.GetTypeInfo(); - foreach (var propertyInfo in typeInfo.DeclaredProperties) - { - if (propertyInfo.Name.Equals(name, StringComparison.Ordinal) - && !(propertyInfo.GetMethod ?? propertyInfo.SetMethod)!.IsStatic) - { - yield return propertyInfo; - } - } - - currentType = typeInfo.BaseType; - } - while (currentType != null); - } - - // Looking up the members through the whole hierarchy allows to find inherited private members. - public static IEnumerable GetMembersInHierarchy(this Type type) - { - var currentType = type; - - do - { - // Do the whole hierarchy for properties first since looking for fields is slower. - foreach (var propertyInfo in currentType.GetRuntimeProperties().Where(pi => !(pi.GetMethod ?? pi.SetMethod)!.IsStatic)) - { - yield return propertyInfo; - } - - foreach (var fieldInfo in currentType.GetRuntimeFields().Where(f => !f.IsStatic)) - { - yield return fieldInfo; - } - - currentType = currentType.BaseType; - } - while (currentType != null); - } - - public static IEnumerable GetMembersInHierarchy( - [DynamicallyAccessedMembers( - DynamicallyAccessedMemberTypes.PublicProperties - | DynamicallyAccessedMemberTypes.NonPublicProperties - | DynamicallyAccessedMemberTypes.PublicFields - | DynamicallyAccessedMemberTypes.NonPublicFields)] - this Type type, - string name) - => type.GetMembersInHierarchy().Where(m => m.Name == name); - - private static readonly Dictionary CommonTypeDictionary = new() - { -#pragma warning disable IDE0034 // Simplify 'default' expression - default causes default(object) - { typeof(int), default(int) }, - { typeof(Guid), default(Guid) }, - { typeof(DateOnly), default(DateOnly) }, - { typeof(DateTime), default(DateTime) }, - { typeof(DateTimeOffset), default(DateTimeOffset) }, - { typeof(TimeOnly), default(TimeOnly) }, - { typeof(long), default(long) }, - { typeof(bool), default(bool) }, - { typeof(double), default(double) }, - { typeof(short), default(short) }, - { typeof(float), default(float) }, - { typeof(byte), default(byte) }, - { typeof(char), default(char) }, - { typeof(uint), default(uint) }, - { typeof(ushort), default(ushort) }, - { typeof(ulong), default(ulong) }, - { typeof(sbyte), default(sbyte) } -#pragma warning restore IDE0034 // Simplify 'default' expression - }; - - public static object? GetDefaultValue( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - this Type type) - { - if (!type.IsValueType) - { - return null; - } - - // A bit of perf code to avoid calling Activator.CreateInstance for common types and - // to avoid boxing on every call. This is about 50% faster than just calling CreateInstance - // for all value types. - return CommonTypeDictionary.TryGetValue(type, out var value) - ? value - : Activator.CreateInstance(type); - } - - [RequiresUnreferencedCode("Gets all types from the given assembly - unsafe for trimming")] - public static IEnumerable GetConstructibleTypes(this Assembly assembly) - => assembly.GetLoadableDefinedTypes().Where( - t => t is { IsAbstract: false, IsGenericTypeDefinition: false }); - - [RequiresUnreferencedCode("Gets all types from the given assembly - unsafe for trimming")] - public static IEnumerable GetLoadableDefinedTypes(this Assembly assembly) - { - try - { - return assembly.DefinedTypes; - } - catch (ReflectionTypeLoadException ex) - { - return ex.Types.Where(t => t != null).Select(IntrospectionExtensions.GetTypeInfo!); - } - } - - /// - /// 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. - /// - public static string DisplayName(this Type type, bool fullName = true, bool compilable = false) - { - var stringBuilder = new StringBuilder(); - ProcessType(stringBuilder, type, fullName, compilable); - return stringBuilder.ToString(); - } - - private static void ProcessType(StringBuilder builder, Type type, bool fullName, bool compilable) - { - if (type.IsGenericType) - { - var genericArguments = type.GetGenericArguments(); - ProcessGenericType(builder, type, genericArguments, genericArguments.Length, fullName, compilable); - } - else if (type.IsArray) - { - ProcessArrayType(builder, type, fullName, compilable); - } - else if (BuiltInTypeNames.TryGetValue(type, out var builtInName)) - { - builder.Append(builtInName); - } - else if (!type.IsGenericParameter) - { - if (compilable) - { - if (type.IsNested) - { - ProcessType(builder, type.DeclaringType!, fullName, compilable); - builder.Append('.'); - } - else if (fullName) - { - builder.Append(type.Namespace).Append('.'); - } - - builder.Append(type.Name); - } - else - { - builder.Append(fullName ? type.FullName : type.Name); - } - } - } - - private static void ProcessArrayType(StringBuilder builder, Type type, bool fullName, bool compilable) - { - var innerType = type; - while (innerType.IsArray) - { - innerType = innerType.GetElementType()!; - } - - ProcessType(builder, innerType, fullName, compilable); - - while (type.IsArray) - { - builder.Append('['); - builder.Append(',', type.GetArrayRank() - 1); - builder.Append(']'); - type = type.GetElementType()!; - } - } - - private static void ProcessGenericType( - StringBuilder builder, - Type type, - Type[] genericArguments, - int length, - bool fullName, - bool compilable) - { - if (type.IsConstructedGenericType - && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - ProcessType(builder, type.UnwrapNullableType(), fullName, compilable); - builder.Append('?'); - return; - } - - var offset = type.IsNested ? type.DeclaringType!.GetGenericArguments().Length : 0; - - if (compilable) - { - if (type.IsNested) - { - ProcessType(builder, type.DeclaringType!, fullName, compilable); - builder.Append('.'); - } - else if (fullName) - { - builder.Append(type.Namespace); - builder.Append('.'); - } - } - else - { - if (fullName) - { - if (type.IsNested) - { - ProcessGenericType(builder, type.DeclaringType!, genericArguments, offset, fullName, compilable); - builder.Append('+'); - } - else - { - builder.Append(type.Namespace); - builder.Append('.'); - } - } - } - - var genericPartIndex = type.Name.IndexOf('`'); - if (genericPartIndex <= 0) - { - builder.Append(type.Name); - return; - } - - builder.Append(type.Name, 0, genericPartIndex); - builder.Append('<'); - - for (var i = offset; i < length; i++) - { - ProcessType(builder, genericArguments[i], fullName, compilable); - if (i + 1 == length) - { - continue; - } - - builder.Append(','); - if (!genericArguments[i + 1].IsGenericParameter) - { - builder.Append(' '); - } - } - - builder.Append('>'); - } - - public static IEnumerable GetNamespaces(this Type type) - { - if (BuiltInTypeNames.ContainsKey(type)) - { - yield break; - } - - if (type.IsArray) - { - foreach (var ns in type.GetElementType()!.GetNamespaces()) - { - yield return ns; - } - - yield break; - } - - yield return type.Namespace!; - - if (type.IsGenericType) - { - foreach (var typeArgument in type.GenericTypeArguments) - { - foreach (var ns in typeArgument.GetNamespaces()) - { - yield return ns; - } - } - } - } - - public static ConstantExpression GetDefaultValueConstant(this Type type) - => (ConstantExpression)GenerateDefaultValueConstantMethod - .MakeGenericMethod(type).Invoke(null, Array.Empty())!; - - private static readonly MethodInfo GenerateDefaultValueConstantMethod = - typeof(SharedTypeExtensions).GetTypeInfo().GetDeclaredMethod(nameof(GenerateDefaultValueConstant))!; - - private static ConstantExpression GenerateDefaultValueConstant() - => Expression.Constant(default(TDefault), typeof(TDefault)); -} diff --git a/src/net/KEFCore/Shared8/StringBuilderExtensions.cs b/src/net/KEFCore/Shared8/StringBuilderExtensions.cs deleted file mode 100644 index 0e0b253f..00000000 --- a/src/net/KEFCore/Shared8/StringBuilderExtensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Globalization; - -namespace System.Text; - -internal static class StringBuilderExtensions -{ - public static StringBuilder AppendJoin( - this StringBuilder stringBuilder, - IEnumerable values, - string separator = ", ") - => stringBuilder.AppendJoin(values, (sb, value) => sb.Append(value), separator); - - public static StringBuilder AppendJoin( - this StringBuilder stringBuilder, - string separator, - params string[] values) - => stringBuilder.AppendJoin(values, (sb, value) => sb.Append(value), separator); - - public static StringBuilder AppendJoin( - this StringBuilder stringBuilder, - IEnumerable values, - Action joinAction, - string separator = ", ") - { - var appended = false; - - foreach (var value in values) - { - joinAction(stringBuilder, value); - stringBuilder.Append(separator); - appended = true; - } - - if (appended) - { - stringBuilder.Length -= separator.Length; - } - - return stringBuilder; - } - - public static StringBuilder AppendJoin( - this StringBuilder stringBuilder, - IEnumerable values, - Func joinFunc, - string separator = ", ") - { - var appended = false; - - foreach (var value in values) - { - if (joinFunc(stringBuilder, value)) - { - stringBuilder.Append(separator); - appended = true; - } - } - - if (appended) - { - stringBuilder.Length -= separator.Length; - } - - return stringBuilder; - } - - public static StringBuilder AppendJoin( - this StringBuilder stringBuilder, - IEnumerable values, - TParam param, - Action joinAction, - string separator = ", ") - { - var appended = false; - - foreach (var value in values) - { - joinAction(stringBuilder, value, param); - stringBuilder.Append(separator); - appended = true; - } - - if (appended) - { - stringBuilder.Length -= separator.Length; - } - - return stringBuilder; - } - - public static void AppendBytes(this StringBuilder builder, byte[] bytes) - { - builder.Append("'0x"); - - for (var i = 0; i < bytes.Length; i++) - { - if (i > 31) - { - builder.Append("..."); - break; - } - - builder.Append(bytes[i].ToString("X2", CultureInfo.InvariantCulture)); - } - - builder.Append('\''); - } -} From 9adc6446809610632810a00efa8855bd5dd48374 Mon Sep 17 00:00:00 2001 From: masesdevelopers <94312179+masesdevelopers@users.noreply.github.com> Date: Tue, 9 Jan 2024 22:50:55 +0100 Subject: [PATCH 3/3] Unified Query Internal --- .../KafkaServiceCollectionExtensions.cs | 4 +- src/net/KEFCore/KEFCore.csproj | 11 +- .../KEFCore/Query/Internal/AnonymousObject.cs | 49 +- .../CollectionResultShaperExpression.cs | 37 + .../Internal/EntityProjectionExpression.cs | 103 +- ...aExpressionTranslatingExpressionVisitor.cs | 577 +++++- ...KafkaProjectionBindingExpressionVisitor.cs | 115 +- .../Internal/KafkaQueryExpression.Helper.cs | 18 +- .../Query/Internal/KafkaQueryExpression.cs | 226 ++- .../KafkaQueryTranslationPreprocessor.cs | 0 ...afkaQueryTranslationPreprocessorFactory.cs | 0 ...yableMethodTranslatingExpressionVisitor.cs | 453 ++++- ...thodTranslatingExpressionVisitorFactory.cs | 13 + ...ingExpressionVisitor.QueryingEnumerable.cs | 35 +- ...erExpressionProcessingExpressionVisitor.cs | 132 +- ...kaShapedQueryCompilingExpressionVisitor.cs | 45 +- ...afkaShapedQueryExpressionVisitorFactory.cs | 14 +- .../Query/Internal/KafkaTableExpression.cs | 42 +- .../Internal/SingleResultShaperExpression.cs | 54 +- .../Query/Internal8/AnonymousObject.cs | 86 - .../CollectionResultShaperExpression.cs | 107 - .../Internal8/EntityProjectionExpression.cs | 191 -- ...aExpressionTranslatingExpressionVisitor.cs | 1725 ----------------- ...KafkaProjectionBindingExpressionVisitor.cs | 532 ----- .../Query/Internal8/KafkaQueryContext.cs | 48 - .../Internal8/KafkaQueryContextFactory.cs | 49 - .../Internal8/KafkaQueryExpression.Helper.cs | 230 --- .../Query/Internal8/KafkaQueryExpression.cs | 1312 ------------- ...yableMethodTranslatingExpressionVisitor.cs | 1476 -------------- ...thodTranslatingExpressionVisitorFactory.cs | 39 - ...ingExpressionVisitor.QueryingEnumerable.cs | 191 -- ...erExpressionProcessingExpressionVisitor.cs | 411 ---- ...kaShapedQueryCompilingExpressionVisitor.cs | 82 - ...afkaShapedQueryExpressionVisitorFactory.cs | 39 - .../Query/Internal8/KafkaTableExpression.cs | 68 - .../Internal8/SingleResultShaperExpression.cs | 105 - 36 files changed, 1582 insertions(+), 7037 deletions(-) rename src/net/KEFCore/Query/{Internal8 => Internal}/KafkaQueryTranslationPreprocessor.cs (100%) rename src/net/KEFCore/Query/{Internal8 => Internal}/KafkaQueryTranslationPreprocessorFactory.cs (100%) delete mode 100644 src/net/KEFCore/Query/Internal8/AnonymousObject.cs delete mode 100644 src/net/KEFCore/Query/Internal8/CollectionResultShaperExpression.cs delete mode 100644 src/net/KEFCore/Query/Internal8/EntityProjectionExpression.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaExpressionTranslatingExpressionVisitor.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaProjectionBindingExpressionVisitor.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryContext.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryContextFactory.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryExpression.Helper.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryExpression.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitor.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaShapedQueryExpressionVisitorFactory.cs delete mode 100644 src/net/KEFCore/Query/Internal8/KafkaTableExpression.cs delete mode 100644 src/net/KEFCore/Query/Internal8/SingleResultShaperExpression.cs diff --git a/src/net/KEFCore/Extensions/KafkaServiceCollectionExtensions.cs b/src/net/KEFCore/Extensions/KafkaServiceCollectionExtensions.cs index 4bac84d2..654ba8a3 100644 --- a/src/net/KEFCore/Extensions/KafkaServiceCollectionExtensions.cs +++ b/src/net/KEFCore/Extensions/KafkaServiceCollectionExtensions.cs @@ -61,10 +61,8 @@ public static IServiceCollection AddEntityFrameworkKafkaDatabase(this IServiceCo .TryAdd() .TryAdd() .TryAdd() - .TryAdd() -#if NET8_0 + .TryAdd() .TryAdd() -#endif .TryAdd(p => p.GetRequiredService()) .TryAddProviderSpecificServices( b => b diff --git a/src/net/KEFCore/KEFCore.csproj b/src/net/KEFCore/KEFCore.csproj index c1b43867..c7b6a003 100644 --- a/src/net/KEFCore/KEFCore.csproj +++ b/src/net/KEFCore/KEFCore.csproj @@ -16,16 +16,7 @@ True False - - - - - - - - - - + diff --git a/src/net/KEFCore/Query/Internal/AnonymousObject.cs b/src/net/KEFCore/Query/Internal/AnonymousObject.cs index 77255dfa..1d77e82f 100644 --- a/src/net/KEFCore/Query/Internal/AnonymousObject.cs +++ b/src/net/KEFCore/Query/Internal/AnonymousObject.cs @@ -19,7 +19,10 @@ * Refer to LICENSE for more information. */ +using JetBrains.Annotations; + namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -30,26 +33,64 @@ public readonly struct AnonymousObject { private readonly object[] _values; + /// + /// 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. + /// public static readonly ConstructorInfo AnonymousObjectCtor = typeof(AnonymousObject).GetTypeInfo() .DeclaredConstructors .Single(c => c.GetParameters().Length == 1); + /// + /// 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. + /// + [UsedImplicitly] public AnonymousObject(object[] values) { _values = values; } + /// + /// 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. + /// public static bool operator ==(AnonymousObject x, AnonymousObject y) => x.Equals(y); + /// + /// 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. + /// public static bool operator !=(AnonymousObject x, AnonymousObject y) => !x.Equals(y); - /// + + /// + /// 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. + /// public override bool Equals(object? obj) - => obj is not null && (obj is AnonymousObject anonymousObject - && _values.SequenceEqual(anonymousObject._values)); - /// + => obj is not null + && (obj is AnonymousObject anonymousObject + && _values.SequenceEqual(anonymousObject._values)); + + /// + /// 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. + /// public override int GetHashCode() { var hash = new HashCode(); diff --git a/src/net/KEFCore/Query/Internal/CollectionResultShaperExpression.cs b/src/net/KEFCore/Query/Internal/CollectionResultShaperExpression.cs index b177f1e9..059df06b 100644 --- a/src/net/KEFCore/Query/Internal/CollectionResultShaperExpression.cs +++ b/src/net/KEFCore/Query/Internal/CollectionResultShaperExpression.cs @@ -20,6 +20,7 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -28,6 +29,12 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; /// public class CollectionResultShaperExpression : Expression, IPrintableExpression { + /// + /// 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. + /// public CollectionResultShaperExpression( Expression projection, Expression innerShaper, @@ -40,12 +47,36 @@ public CollectionResultShaperExpression( ElementType = elementType; } + /// + /// 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. + /// public virtual Expression Projection { get; } + /// + /// 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. + /// public virtual Expression InnerShaper { get; } + /// + /// 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. + /// public virtual INavigationBase? Navigation { get; } + /// + /// 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. + /// public virtual Type ElementType { get; } /// @@ -65,6 +96,12 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(projection, innerShaper); } + /// + /// 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. + /// public virtual CollectionResultShaperExpression Update( Expression projection, Expression innerShaper) diff --git a/src/net/KEFCore/Query/Internal/EntityProjectionExpression.cs b/src/net/KEFCore/Query/Internal/EntityProjectionExpression.cs index 8d296ce5..8b5b1850 100644 --- a/src/net/KEFCore/Query/Internal/EntityProjectionExpression.cs +++ b/src/net/KEFCore/Query/Internal/EntityProjectionExpression.cs @@ -22,6 +22,7 @@ using MASES.EntityFrameworkCore.KNet.Internal; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -31,8 +32,18 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; public class EntityProjectionExpression : Expression, IPrintableExpression { private readonly IReadOnlyDictionary _readExpressionMap; +#if NET8_0_OR_GREATER + private readonly Dictionary _navigationExpressionsCache = new(); +#else private readonly Dictionary _navigationExpressionsCache = new(); - +#endif + + /// + /// 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. + /// public EntityProjectionExpression( IEntityType entityType, IReadOnlyDictionary readExpressionMap) @@ -41,14 +52,38 @@ public EntityProjectionExpression( _readExpressionMap = readExpressionMap; } + /// + /// 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. + /// public virtual IEntityType EntityType { get; } + /// + /// 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. + /// public override Type Type => EntityType.ClrType; + /// + /// 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. + /// public sealed override ExpressionType NodeType => ExpressionType.Extension; + /// + /// 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. + /// public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedType) { if (!derivedType.GetAllBaseTypes().Contains(EntityType)) @@ -61,8 +96,13 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy var readExpressionMap = new Dictionary(); foreach (var (property, methodCallExpression) in _readExpressionMap) { +#if NET8_0_OR_GREATER + if (derivedType.IsAssignableFrom(property.DeclaringType) + || property.DeclaringType.IsAssignableFrom(derivedType)) +#else if (derivedType.IsAssignableFrom(property.DeclaringEntityType) || property.DeclaringEntityType.IsAssignableFrom(derivedType)) +#endif { readExpressionMap[property] = methodCallExpression; } @@ -71,10 +111,24 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy return new EntityProjectionExpression(derivedType, readExpressionMap); } + /// + /// 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. + /// public virtual MethodCallExpression BindProperty(IProperty property) { - if (!EntityType.IsAssignableFrom(property.DeclaringEntityType) - && !property.DeclaringEntityType.IsAssignableFrom(EntityType)) + if (property.DeclaringType is not IEntityType entityType) + { + if (EntityType != property.DeclaringType) + { + throw new InvalidOperationException( + KafkaStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName())); + } + } + else if (!EntityType.IsAssignableFrom(entityType) + && !entityType.IsAssignableFrom(EntityType)) { throw new InvalidOperationException( KafkaStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName())); @@ -83,7 +137,17 @@ public virtual MethodCallExpression BindProperty(IProperty property) return _readExpressionMap[property]; } - public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExpression entityShaper) + /// + /// 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. + /// +#if NET8_0_OR_GREATER + public virtual void AddNavigationBinding(INavigation navigation, StructuralTypeShaperExpression shaper) +#else + public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExpression shaper) +#endif { if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) && !navigation.DeclaringEntityType.IsAssignableFrom(EntityType)) @@ -92,10 +156,20 @@ public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExp KafkaStrings.UnableToBindMemberToEntityProjection("navigation", navigation.Name, EntityType.DisplayName())); } - _navigationExpressionsCache[navigation] = entityShaper; + _navigationExpressionsCache[navigation] = shaper; } + /// + /// 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. + /// +#if NET8_0_OR_GREATER + public virtual StructuralTypeShaperExpression? BindNavigation(INavigation navigation) +#else public virtual EntityShaperExpression? BindNavigation(INavigation navigation) +#endif { if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) && !navigation.DeclaringEntityType.IsAssignableFrom(EntityType)) @@ -109,21 +183,40 @@ public virtual void AddNavigationBinding(INavigation navigation, EntityShaperExp : null; } + /// + /// 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. + /// public virtual EntityProjectionExpression Clone() { var readExpressionMap = new Dictionary(_readExpressionMap); var entityProjectionExpression = new EntityProjectionExpression(EntityType, readExpressionMap); foreach (var (navigation, entityShaperExpression) in _navigationExpressionsCache) { +#if NET8_0_OR_GREATER + entityProjectionExpression._navigationExpressionsCache[navigation] = new StructuralTypeShaperExpression( + entityShaperExpression.StructuralType, + ((EntityProjectionExpression)entityShaperExpression.ValueBufferExpression).Clone(), + entityShaperExpression.IsNullable); +#else entityProjectionExpression._navigationExpressionsCache[navigation] = new EntityShaperExpression( entityShaperExpression.EntityType, ((EntityProjectionExpression)entityShaperExpression.ValueBufferExpression).Clone(), entityShaperExpression.IsNullable); +#endif } return entityProjectionExpression; } + /// + /// 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. + /// void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) { expressionPrinter.AppendLine(nameof(EntityProjectionExpression) + ":"); diff --git a/src/net/KEFCore/Query/Internal/KafkaExpressionTranslatingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal/KafkaExpressionTranslatingExpressionVisitor.cs index 16e6b3e3..8caccdb1 100644 --- a/src/net/KEFCore/Query/Internal/KafkaExpressionTranslatingExpressionVisitor.cs +++ b/src/net/KEFCore/Query/Internal/KafkaExpressionTranslatingExpressionVisitor.cs @@ -23,9 +23,11 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; +using JetBrains.Annotations; using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -80,6 +82,8 @@ public class KafkaExpressionTranslatingExpressionVisitor : ExpressionVisitor private static readonly MethodInfo KafkaLikeMethodInfo = typeof(KafkaExpressionTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(KafkaLike))!; + private static readonly MethodInfo GetTypeMethodInfo = typeof(object).GetTypeInfo().GetDeclaredMethod(nameof(GetType))!; + // Regex special chars defined here: // https://msdn.microsoft.com/en-us/library/4edbef7e(v=vs.110).aspx private static readonly char[] RegexSpecialChars @@ -97,6 +101,12 @@ private static string BuildEscapeRegexCharsPattern(IEnumerable regexSpecia private readonly EntityReferenceFindingExpressionVisitor _entityReferenceFindingExpressionVisitor; private readonly IModel _model; + /// + /// 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. + /// public KafkaExpressionTranslatingExpressionVisitor( QueryCompilationContext queryCompilationContext, QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) @@ -107,8 +117,20 @@ public KafkaExpressionTranslatingExpressionVisitor( _model = queryCompilationContext.Model; } + /// + /// 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. + /// public virtual string? TranslationErrorDetails { get; private set; } + /// + /// 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. + /// protected virtual void AddTranslationErrorDetails(string details) { if (TranslationErrorDetails == null) @@ -121,6 +143,12 @@ protected virtual void AddTranslationErrorDetails(string details) } } + /// + /// 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. + /// public virtual Expression? Translate(Expression expression) { TranslationErrorDetails = null; @@ -138,16 +166,21 @@ protected virtual void AddTranslationErrorDetails(string details) : result; } + /// + /// 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. + /// protected override Expression VisitBinary(BinaryExpression binaryExpression) { if (binaryExpression.Left.Type == typeof(object[]) - && binaryExpression.Left is NewArrayExpression - && binaryExpression.NodeType == ExpressionType.Equal) + && binaryExpression is { Left: NewArrayExpression, NodeType: ExpressionType.Equal }) { return Visit(ConvertObjectArrayEqualityComparison(binaryExpression.Left, binaryExpression.Right)); } - if ((binaryExpression.NodeType == ExpressionType.Equal || binaryExpression.NodeType == ExpressionType.NotEqual) + if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual && (binaryExpression.Left.IsNullConstantExpression() || binaryExpression.Right.IsNullConstantExpression())) { var nonNullExpression = binaryExpression.Left.IsNullConstantExpression() ? binaryExpression.Right : binaryExpression.Left; @@ -170,7 +203,12 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) { var projection = translatedSubquery.ShaperExpression; if (projection is NewExpression - || RemoveConvert(projection) is EntityShaperExpression { IsNullable: false }) +#if NET8_0_OR_GREATER + || RemoveConvert(projection) is StructuralTypeShaperExpression { IsNullable: false } +#else + || RemoveConvert(projection) is EntityShaperExpression { IsNullable: false } +#endif + || RemoveConvert(projection) is CollectionResultShaperExpression) { var anySubquery = Expression.Call( QueryableMethods.AnyWithoutPredicate.MakeGenericMethod(translatedSubquery.Type.GetSequenceType()), @@ -190,6 +228,23 @@ static Expression RemoveConvert(Expression e) } } + if (binaryExpression.NodeType == ExpressionType.Equal + || binaryExpression.NodeType == ExpressionType.NotEqual + && binaryExpression.Left.Type == typeof(Type)) + { + if (IsGetTypeMethodCall(binaryExpression.Left, out var entityReference1) + && IsTypeConstant(binaryExpression.Right, out var type1)) + { + return ProcessGetType(entityReference1!, type1!, binaryExpression.NodeType == ExpressionType.Equal); + } + + if (IsGetTypeMethodCall(binaryExpression.Right, out var entityReference2) + && IsTypeConstant(binaryExpression.Left, out var type2)) + { + return ProcessGetType(entityReference2!, type2!, binaryExpression.NodeType == ExpressionType.Equal); + } + } + var newLeft = Visit(binaryExpression.Left); var newRight = Visit(binaryExpression.Right); @@ -199,8 +254,7 @@ static Expression RemoveConvert(Expression e) return QueryCompilationContext.NotTranslatedExpression; } - if ((binaryExpression.NodeType == ExpressionType.Equal - || binaryExpression.NodeType == ExpressionType.NotEqual) + if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual // Visited expression could be null, We need to pass MemberInitExpression && TryRewriteEntityEquality( binaryExpression.NodeType, @@ -219,26 +273,15 @@ static Expression RemoveConvert(Expression e) newRight = ConvertToNullable(newRight); } - if (binaryExpression.NodeType == ExpressionType.Equal - || binaryExpression.NodeType == ExpressionType.NotEqual) + if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual + && TryUseComparer(newLeft, newRight, out var updatedExpression)) { - var property = FindProperty(newLeft) ?? FindProperty(newRight); - var comparer = property?.GetValueComparer(); - - if (comparer != null - && comparer.Type.IsAssignableFrom(newLeft.Type) - && comparer.Type.IsAssignableFrom(newRight.Type)) + if (binaryExpression.NodeType == ExpressionType.NotEqual) { - if (binaryExpression.NodeType == ExpressionType.Equal) - { - return comparer.ExtractEqualsBody(newLeft, newRight); - } - - if (binaryExpression.NodeType == ExpressionType.NotEqual) - { - return Expression.IsFalse(comparer.ExtractEqualsBody(newLeft, newRight)); - } + updatedExpression = Expression.IsFalse(updatedExpression!); } + + return updatedExpression!; } return Expression.MakeBinary( @@ -248,8 +291,183 @@ static Expression RemoveConvert(Expression e) binaryExpression.IsLiftedToNull, binaryExpression.Method, binaryExpression.Conversion); + + Expression ProcessGetType(StructuralTypeReferenceExpression typeReference, Type comparisonType, bool match) + { + if (typeReference.StructuralType is not IEntityType entityType + || (entityType.BaseType == null + && !entityType.GetDirectlyDerivedTypes().Any())) + { + // No hierarchy + return Expression.Constant((typeReference.StructuralType.ClrType == comparisonType) == match); + } + + if (entityType.GetAllBaseTypes().Any(e => e.ClrType == comparisonType)) + { + // EntitySet will never contain a type of base type + return Expression.Constant(!match); + } + + var derivedType = entityType.GetDerivedTypesInclusive().SingleOrDefault(et => et.ClrType == comparisonType); + // If no derived type matches then fail the translation + if (derivedType != null) + { + // If the derived type is abstract type then predicate will always be false + if (derivedType.IsAbstract()) + { + return Expression.Constant(!match); + } + + // Or add predicate for matching that particular type discriminator value + // All hierarchies have discriminator property + var discriminatorProperty = entityType.FindDiscriminatorProperty()!; + var boundProperty = BindProperty(typeReference, discriminatorProperty, discriminatorProperty.ClrType); + // KeyValueComparer is not null at runtime + var valueComparer = discriminatorProperty.GetKeyValueComparer(); + + var result = valueComparer.ExtractEqualsBody( + boundProperty!, + Expression.Constant(derivedType.GetDiscriminatorValue(), discriminatorProperty.ClrType)); + + return match ? result : Expression.Not(result); + } + + return QueryCompilationContext.NotTranslatedExpression; + } + + bool IsGetTypeMethodCall(Expression expression, out StructuralTypeReferenceExpression? typeReference) + { + typeReference = null; + if (expression is not MethodCallExpression methodCallExpression + || methodCallExpression.Method != GetTypeMethodInfo) + { + return false; + } + + typeReference = Visit(methodCallExpression.Object) as StructuralTypeReferenceExpression; + return typeReference != null; + } + + static bool IsTypeConstant(Expression expression, out Type? type) + { + type = null; + if (expression is not UnaryExpression + { + NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked, + Operand: ConstantExpression constantExpression + }) + { + return false; + } + + type = constantExpression.Value as Type; + return type != null; + } + } + + private static bool TryUseComparer( + Expression? newLeft, + Expression? newRight, + out Expression? updatedExpression) + { + updatedExpression = null; + + if (newLeft == null + || newRight == null) + { + return false; + } + + var property = FindProperty(newLeft) ?? FindProperty(newRight); + var comparer = property?.GetValueComparer(); + + if (comparer == null) + { + return false; + } + + MethodInfo? objectEquals = null; + MethodInfo? exactMatch = null; + + var converter = property?.GetValueConverter(); + foreach (var candidate in comparer + .GetType() + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where( + m => m.Name == "Equals" && m.GetParameters().Length == 2) + .ToList()) + { + var parameters = candidate.GetParameters(); + var leftType = parameters[0].ParameterType; + var rightType = parameters[1].ParameterType; + + if (leftType == typeof(object) + && rightType == typeof(object)) + { + objectEquals = candidate; + continue; + } + + var matchingLeft = leftType.IsAssignableFrom(newLeft.Type) + ? newLeft + : converter != null + && leftType.IsAssignableFrom(converter.ModelClrType) + && converter.ProviderClrType.IsAssignableFrom(newLeft.Type) + ? ReplacingExpressionVisitor.Replace( + converter.ConvertFromProviderExpression.Parameters.Single(), + newLeft, + converter.ConvertFromProviderExpression.Body) + : null; + + var matchingRight = rightType.IsAssignableFrom(newRight.Type) + ? newRight + : converter != null + && rightType.IsAssignableFrom(converter.ModelClrType) + && converter.ProviderClrType.IsAssignableFrom(newRight.Type) + ? ReplacingExpressionVisitor.Replace( + converter.ConvertFromProviderExpression.Parameters.Single(), + newRight, + converter.ConvertFromProviderExpression.Body) + : null; + + if (matchingLeft != null && matchingRight != null) + { + exactMatch = candidate; + newLeft = matchingLeft; + newRight = matchingRight; + break; + } + } + + if (exactMatch == null + && (!property!.ClrType.IsAssignableFrom(newLeft.Type)) + || !property!.ClrType.IsAssignableFrom(newRight.Type)) + { + return false; + } + + updatedExpression = + exactMatch != null + ? Expression.Call( + Expression.Constant(comparer, comparer.GetType()), + exactMatch, + newLeft, + newRight) + : Expression.Call( + Expression.Constant(comparer, comparer.GetType()), + objectEquals!, + Expression.Convert(newLeft, typeof(object)), + Expression.Convert(newRight, typeof(object))); + + return true; } + /// + /// 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. + /// protected override Expression VisitConditional(ConditionalExpression conditionalExpression) { var test = Visit(conditionalExpression.Test); @@ -278,17 +496,27 @@ protected override Expression VisitConditional(ConditionalExpression conditional return Expression.Condition(test, ifTrue, ifFalse); } + /// + /// 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. + /// protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) { case EntityProjectionExpression: - case EntityReferenceExpression: - return extensionExpression; + case StructuralTypeReferenceExpression: + return extensionExpression; +#if NET8_0_OR_GREATER + case StructuralTypeShaperExpression shaper: + return new StructuralTypeReferenceExpression(shaper); +#else case EntityShaperExpression entityShaperExpression: - return new EntityReferenceExpression(entityShaperExpression); - + return new StructuralTypeReferenceExpression(entityShaperExpression); +#endif case ProjectionBindingExpression projectionBindingExpression: return ((KafkaQueryExpression)projectionBindingExpression.QueryExpression) .GetProjection(projectionBindingExpression); @@ -298,15 +526,39 @@ protected override Expression VisitExtension(Expression extensionExpression) } } + /// + /// 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. + /// protected override Expression VisitInvocation(InvocationExpression invocationExpression) => QueryCompilationContext.NotTranslatedExpression; + /// + /// 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. + /// protected override Expression VisitLambda(Expression lambdaExpression) => throw new InvalidOperationException(CoreStrings.TranslationFailed(lambdaExpression.Print())); + /// + /// 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. + /// protected override Expression VisitListInit(ListInitExpression listInitExpression) => QueryCompilationContext.NotTranslatedExpression; + /// + /// 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. + /// protected override Expression VisitMember(MemberExpression memberExpression) { var innerExpression = Visit(memberExpression.Expression); @@ -341,6 +593,7 @@ protected override Expression VisitMember(MemberExpression memberExpression) updatedMemberExpression = ConvertToNullable(updatedMemberExpression); return Expression.Condition( + // Since inner is nullable type this is fine. Expression.Equal(innerExpression, Expression.Default(innerExpression.Type)), Expression.Default(updatedMemberExpression.Type), updatedMemberExpression); @@ -351,9 +604,15 @@ protected override Expression VisitMember(MemberExpression memberExpression) static bool ShouldApplyNullProtectionForMemberAccess(Type callerType, string memberName) => !(callerType.IsGenericType && callerType.GetGenericTypeDefinition() == typeof(Nullable<>) - && (memberName == nameof(Nullable.Value) || memberName == nameof(Nullable.HasValue))); + && memberName is nameof(Nullable.Value) or nameof(Nullable.HasValue)); } + /// + /// 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. + /// protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment) { var expression = Visit(memberAssignment.Expression); @@ -370,6 +629,12 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment membe return memberAssignment.Update(expression); } + /// + /// 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. + /// protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression) { var newExpression = Visit(memberInitExpression.NewExpression); @@ -387,8 +652,7 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp } newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]); - if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert + if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression && unaryExpression.Operand == QueryCompilationContext.NotTranslatedExpression) { return QueryCompilationContext.NotTranslatedExpression; @@ -398,6 +662,12 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp return memberInitExpression.Update((NewExpression)newExpression, newBindings); } + /// + /// 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. + /// protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.IsGenericMethod @@ -433,18 +703,22 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var shaperExpression = subqueryTranslation.ShaperExpression; var innerExpression = shaperExpression; Type? convertedType = null; - if (shaperExpression is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert) + if (shaperExpression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression) { convertedType = unaryExpression.Type; innerExpression = unaryExpression.Operand; } - +#if NET8_0_OR_GREATER + if (innerExpression is StructuralTypeShaperExpression shaper + && (convertedType == null + || convertedType.IsAssignableFrom(shaper.Type))) +#else if (innerExpression is EntityShaperExpression entityShaperExpression && (convertedType == null || convertedType.IsAssignableFrom(entityShaperExpression.Type))) +#endif { - return new EntityReferenceExpression(subqueryTranslation.UpdateShaperExpression(innerExpression)); + return new StructuralTypeReferenceExpression(subqueryTranslation.UpdateShaperExpression(innerExpression)); } if (!(innerExpression is ProjectionBindingExpression projectionBindingExpression @@ -497,8 +771,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var method = methodCallExpression.Method; if (method.Name == nameof(object.Equals) - && methodCallExpression.Object != null - && methodCallExpression.Arguments.Count == 1) + && methodCallExpression is { Object: not null, Arguments.Count: 1 }) { var left = Visit(methodCallExpression.Object); var right = Visit(methodCallExpression.Arguments[0]); @@ -537,6 +810,11 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var left = Visit(methodCallExpression.Arguments[0]); var right = Visit(methodCallExpression.Arguments[1]); + if (TryUseComparer(left, right, out var updatedExpression)) + { + return updatedExpression!; + } + if (TryRewriteEntityEquality( ExpressionType.Equal, left == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[0] : left, @@ -677,6 +955,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return methodCallExpression.Update(@object, arguments); } + /// + /// 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. + /// protected override Expression VisitNew(NewExpression newExpression) { var newArguments = new List(); @@ -699,6 +983,12 @@ protected override Expression VisitNew(NewExpression newExpression) return newExpression.Update(newArguments); } + /// + /// 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. + /// protected override Expression VisitNewArray(NewArrayExpression newArrayExpression) { var newExpressions = new List(); @@ -721,6 +1011,12 @@ protected override Expression VisitNewArray(NewArrayExpression newArrayExpressio return newArrayExpression.Update(newExpressions); } + /// + /// 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. + /// protected override Expression VisitParameter(ParameterExpression parameterExpression) { if (parameterExpression.Name?.StartsWith(QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal) == true) @@ -734,12 +1030,21 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres throw new InvalidOperationException(CoreStrings.TranslationFailed(parameterExpression.Print())); } + /// + /// 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. + /// protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) { if (typeBinaryExpression.NodeType == ExpressionType.TypeIs - && Visit(typeBinaryExpression.Expression) is EntityReferenceExpression entityReferenceExpression) + && Visit(typeBinaryExpression.Expression) is StructuralTypeReferenceExpression typeReference) { - var entityType = entityReferenceExpression.EntityType; + if (typeReference.StructuralType is not IEntityType entityType) + { + return Expression.Constant(typeReference.StructuralType.ClrType == typeBinaryExpression.TypeOperand); + } if (entityType.GetAllBaseTypesInclusive().Any(et => et.ClrType == typeBinaryExpression.TypeOperand)) { @@ -751,7 +1056,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp { // All hierarchies have discriminator property var discriminatorProperty = entityType.FindDiscriminatorProperty()!; - var boundProperty = BindProperty(entityReferenceExpression, discriminatorProperty, discriminatorProperty.ClrType); + var boundProperty = BindProperty(typeReference, discriminatorProperty, discriminatorProperty.ClrType); // KeyValueComparer is not null at runtime var valueComparer = discriminatorProperty.GetKeyValueComparer(); @@ -775,6 +1080,12 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp return QueryCompilationContext.NotTranslatedExpression; } + /// + /// 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. + /// protected override Expression VisitUnary(UnaryExpression unaryExpression) { var newOperand = Visit(unaryExpression.Operand); @@ -783,12 +1094,10 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) return QueryCompilationContext.NotTranslatedExpression; } - if (newOperand is EntityReferenceExpression entityReferenceExpression - && (unaryExpression.NodeType == ExpressionType.Convert - || unaryExpression.NodeType == ExpressionType.ConvertChecked - || unaryExpression.NodeType == ExpressionType.TypeAs)) + if (newOperand is StructuralTypeReferenceExpression typeReference + && unaryExpression.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs) { - return entityReferenceExpression.Convert(unaryExpression.Type); + return typeReference.Convert(unaryExpression.Type); } if (unaryExpression.NodeType == ExpressionType.Convert @@ -804,10 +1113,11 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) } var result = (Expression)Expression.MakeUnary(unaryExpression.NodeType, newOperand, unaryExpression.Type); - if (result is UnaryExpression outerUnary - && outerUnary.NodeType == ExpressionType.Convert - && outerUnary.Operand is UnaryExpression innerUnary - && innerUnary.NodeType == ExpressionType.Convert) + if (result is UnaryExpression + { + NodeType: ExpressionType.Convert, + Operand: UnaryExpression { NodeType: ExpressionType.Convert } innerUnary + } outerUnary) { var innerMostType = innerUnary.Operand.Type; var intermediateType = innerUnary.Type; @@ -830,12 +1140,12 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) private Expression? TryBindMember(Expression? source, MemberIdentity member, Type type) { - if (!(source is EntityReferenceExpression entityReferenceExpression)) + if (source is not StructuralTypeReferenceExpression typeReference) { return null; } - var entityType = entityReferenceExpression.EntityType; + var entityType = typeReference.StructuralType; var property = member.MemberInfo != null ? entityType.FindProperty(member.MemberInfo) @@ -843,22 +1153,22 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) if (property != null) { - return BindProperty(entityReferenceExpression, property, type); + return BindProperty(typeReference, property, type); } AddTranslationErrorDetails( CoreStrings.QueryUnableToTranslateMember( member.Name, - entityReferenceExpression.EntityType.DisplayName())); + typeReference.StructuralType.DisplayName())); return null; } - private Expression? BindProperty(EntityReferenceExpression entityReferenceExpression, IProperty property, Type type) + private Expression? BindProperty(StructuralTypeReferenceExpression typeReference, IProperty property, Type type) { - if (entityReferenceExpression.ParameterEntity != null) + if (typeReference.Parameter != null) { - var valueBufferExpression = Visit(entityReferenceExpression.ParameterEntity.ValueBufferExpression); + var valueBufferExpression = Visit(typeReference.Parameter.ValueBufferExpression); if (valueBufferExpression == QueryCompilationContext.NotTranslatedExpression) { return null; @@ -876,10 +1186,14 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) : result; } - if (entityReferenceExpression.SubqueryEntity != null) + if (typeReference.Subquery != null) { - var entityShaper = (EntityShaperExpression)entityReferenceExpression.SubqueryEntity.ShaperExpression; - var kafkaQueryExpression = (KafkaQueryExpression)entityReferenceExpression.SubqueryEntity.QueryExpression; +#if NET8_0_OR_GREATER + var entityShaper = (StructuralTypeShaperExpression)typeReference.Subquery.ShaperExpression; +#else + var entityShaper = (EntityShaperExpression)typeReference.Subquery.ShaperExpression; +#endif + var kafkaQueryExpression = (KafkaQueryExpression)typeReference.Subquery.QueryExpression; var projectionBindingExpression = (ProjectionBindingExpression)entityShaper.ValueBufferExpression; var entityProjectionExpression = (EntityProjectionExpression)kafkaQueryExpression.GetProjection( @@ -912,8 +1226,7 @@ private static Expression ProcessSingleResultScalar( var serverQuery = kafkaQueryExpression.ServerQueryExpression; serverQuery = ((LambdaExpression)((NewExpression)serverQuery).Arguments[0]).Body; - if (serverQuery is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert + if (serverQuery is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression && unaryExpression.Type == typeof(object)) { serverQuery = unaryExpression.Operand; @@ -930,6 +1243,7 @@ private static Expression ProcessSingleResultScalar( readExpression)); } + [UsedImplicitly] private static T GetParameterValue(QueryContext queryContext, string parameterName) => (T)queryContext.ParameterValues[parameterName]!; @@ -948,18 +1262,24 @@ private static Expression ConvertToNonNullable(Expression expression) ? Expression.Convert(expression, expression.Type.UnwrapNullableType()) : expression; - private static IProperty? FindProperty(Expression expression) + private static IProperty? FindProperty(Expression? expression) { - if (expression.NodeType == ExpressionType.Convert + if (expression?.NodeType == ExpressionType.Convert + && expression.Type == typeof(object)) + { + expression = ((UnaryExpression)expression).Operand; + } + + if (expression?.NodeType == ExpressionType.Convert && expression.Type.IsNullableType() && expression is UnaryExpression unaryExpression - && expression.Type.UnwrapNullableType() == unaryExpression.Type) + && (expression.Type.UnwrapNullableType() == unaryExpression.Type + || expression.Type == unaryExpression.Type)) { expression = unaryExpression.Operand; } - if (expression is MethodCallExpression readValueMethodCall - && readValueMethodCall.Method.IsGenericMethod + if (expression is MethodCallExpression { Method.IsGenericMethod: true } readValueMethodCall && readValueMethodCall.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod) { return readValueMethodCall.Arguments[2].GetConstantValue(); @@ -972,12 +1292,11 @@ private bool TryRewriteContainsEntity(Expression? source, Expression item, [NotN { result = null; - if (!(item is EntityReferenceExpression itemEntityReference)) + if (item is not StructuralTypeReferenceExpression { StructuralType: IEntityType entityType }) { return false; } - var entityType = itemEntityReference.EntityType; var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties; if (primaryKeyProperties == null) { @@ -1010,9 +1329,8 @@ private bool TryRewriteContainsEntity(Expression? source, Expression item, [NotN rewrittenSource = Expression.Constant(propertyValueList); break; - case MethodCallExpression methodCallExpression - when methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: + case MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression + when methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: var parameterName = methodCallExpression.Arguments[1].GetConstantValue(); var lambda = Expression.Lambda( Expression.Call( @@ -1050,8 +1368,8 @@ private bool TryRewriteEntityEquality( bool equalsMethod, [NotNullWhen(true)] out Expression? result) { - var leftEntityReference = left as EntityReferenceExpression; - var rightEntityReference = right as EntityReferenceExpression; + var leftEntityReference = left is StructuralTypeReferenceExpression { StructuralType: IEntityType } l ? l : null; + var rightEntityReference = right is StructuralTypeReferenceExpression { StructuralType: IEntityType } r ? r : null; if (leftEntityReference == null && rightEntityReference == null) @@ -1064,7 +1382,7 @@ private bool TryRewriteEntityEquality( || IsNullConstantExpression(right)) { var nonNullEntityReference = (IsNullConstantExpression(left) ? rightEntityReference : leftEntityReference)!; - var entityType1 = nonNullEntityReference.EntityType; + var entityType1 = (IEntityType)nonNullEntityReference.StructuralType; var primaryKeyProperties1 = entityType1.FindPrimaryKey()?.Properties; if (primaryKeyProperties1 == null) { @@ -1089,8 +1407,8 @@ private bool TryRewriteEntityEquality( return true; } - var leftEntityType = leftEntityReference?.EntityType; - var rightEntityType = rightEntityReference?.EntityType; + var leftEntityType = (IEntityType?)leftEntityReference?.StructuralType; + var rightEntityType = (IEntityType?)rightEntityReference?.StructuralType; var entityType = leftEntityType ?? rightEntityType; Check.DebugAssert(entityType != null, "At least either side should be entityReference so entityType should be non-null."); @@ -1117,8 +1435,8 @@ private bool TryRewriteEntityEquality( } if (primaryKeyProperties.Count > 1 - && (leftEntityReference?.SubqueryEntity != null - || rightEntityReference?.SubqueryEntity != null)) + && (leftEntityReference?.Subquery != null + || rightEntityReference?.Subquery != null)) { throw new InvalidOperationException( CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported( @@ -1156,9 +1474,8 @@ constantExpression.Value is null : property.GetGetter().GetClrValue(constantExpression.Value), property.ClrType.MakeNullable()); - case MethodCallExpression methodCallExpression - when methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: + case MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression + when methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: var parameterName = methodCallExpression.Arguments[1].GetConstantValue(); var lambda = Expression.Lambda( Expression.Call( @@ -1216,18 +1533,22 @@ when CanEvaluate(memberInitExpression): private static ConstantExpression GetValue(Expression expression) => Expression.Constant( - Expression.Lambda>(Expression.Convert(expression, typeof(object))).Compile().Invoke(), + Expression.Lambda>(Expression.Convert(expression, typeof(object))) + .Compile(preferInterpretation: true) + .Invoke(), expression.Type); private static bool CanEvaluate(Expression expression) { +#pragma warning disable IDE0066 // Convert switch statement to expression switch (expression) +#pragma warning restore IDE0066 // Convert switch statement to expression { case ConstantExpression: return true; case NewExpression newExpression: - return newExpression.Arguments.All(e => CanEvaluate(e)); + return newExpression.Arguments.All(CanEvaluate); case MemberInitExpression memberInitExpression: return CanEvaluate(memberInitExpression.NewExpression) @@ -1258,8 +1579,11 @@ private static Expression ConvertObjectArrayEqualityComparison(Expression left, { l = l.Type.IsNullableType() ? l : Expression.Convert(l, r.Type); } - +#if NET7_0_OR_GREATER + return ExpressionExtensions.CreateEqualsExpression(l, r); +#else return Expression.Equal(l, r); +#endif }) .Aggregate((a, b) => Expression.AndAlso(a, b)); @@ -1272,12 +1596,12 @@ static Expression RemoveObjectConvert(Expression expression) } private static bool IsNullConstantExpression(Expression expression) - => expression is ConstantExpression constantExpression && constantExpression.Value == null; + => expression is ConstantExpression { Value: null }; [DebuggerStepThrough] private static bool TranslationFailed(Expression? original, Expression? translation) => original != null - && (translation == QueryCompilationContext.NotTranslatedExpression || translation is EntityReferenceExpression); + && (translation == QueryCompilationContext.NotTranslatedExpression || translation is StructuralTypeReferenceExpression); private static bool KafkaLike(string matchExpression, string pattern, string escapeCharacter) { @@ -1381,7 +1705,7 @@ public bool Find(Expression expression) return expression; } - if (expression is EntityReferenceExpression) + if (expression is StructuralTypeReferenceExpression) { _found = true; return expression; @@ -1390,34 +1714,80 @@ public bool Find(Expression expression) return base.Visit(expression); } } +#if NET8_0_OR_GREATER + private sealed class StructuralTypeReferenceExpression : Expression + { + public StructuralTypeReferenceExpression(StructuralTypeShaperExpression parameter) + { + Parameter = parameter; + StructuralType = parameter.StructuralType; + } + + public StructuralTypeReferenceExpression(ShapedQueryExpression subquery) + { + Subquery = subquery; + StructuralType = ((StructuralTypeShaperExpression)subquery.ShaperExpression).StructuralType; + } + + private StructuralTypeReferenceExpression(StructuralTypeReferenceExpression typeReference, IEntityType type) + { + Parameter = typeReference.Parameter; + Subquery = typeReference.Subquery; + StructuralType = type; + } - private sealed class EntityReferenceExpression : Expression + public new StructuralTypeShaperExpression? Parameter { get; } + public ShapedQueryExpression? Subquery { get; } + public ITypeBase StructuralType { get; } + + public override Type Type + => StructuralType.ClrType; + + public override ExpressionType NodeType + => ExpressionType.Extension; + + public Expression Convert(Type type) + { + if (type == typeof(object) // Ignore object conversion + || type.IsAssignableFrom(Type)) // Ignore casting to base type/interface + { + return this; + } + + return StructuralType is IEntityType entityType + && entityType.GetDerivedTypes().FirstOrDefault(et => et.ClrType == type) is IEntityType derivedEntityType + ? new StructuralTypeReferenceExpression(this, derivedEntityType) + : QueryCompilationContext.NotTranslatedExpression; + } + } +#else + private sealed class StructuralTypeReferenceExpression : Expression { - public EntityReferenceExpression(EntityShaperExpression parameter) + public StructuralTypeReferenceExpression(EntityShaperExpression parameter) { - ParameterEntity = parameter; - EntityType = parameter.EntityType; + Parameter = parameter; + StructuralType = parameter.EntityType; } - public EntityReferenceExpression(ShapedQueryExpression subquery) + public StructuralTypeReferenceExpression(ShapedQueryExpression subquery) { - SubqueryEntity = subquery; - EntityType = ((EntityShaperExpression)subquery.ShaperExpression).EntityType; + Subquery = subquery; + StructuralType = ((EntityShaperExpression)subquery.ShaperExpression).EntityType; } - private EntityReferenceExpression(EntityReferenceExpression entityReferenceExpression, IEntityType entityType) + private StructuralTypeReferenceExpression(StructuralTypeReferenceExpression entityReferenceExpression, IEntityType entityType) { - ParameterEntity = entityReferenceExpression.ParameterEntity; - SubqueryEntity = entityReferenceExpression.SubqueryEntity; - EntityType = entityType; + Parameter = entityReferenceExpression.Parameter; + Subquery = entityReferenceExpression.Subquery; + StructuralType = entityType; } - public EntityShaperExpression? ParameterEntity { get; } - public ShapedQueryExpression? SubqueryEntity { get; } - public IEntityType EntityType { get; } + public new EntityShaperExpression? Parameter { get; } + public ShapedQueryExpression? Subquery { get; } + public IEntityType StructuralType { get; } public override Type Type - => EntityType.ClrType; + => StructuralType.ClrType; public override ExpressionType NodeType => ExpressionType.Extension; @@ -1430,11 +1800,12 @@ public Expression Convert(Type type) return this; } - var derivedEntityType = EntityType.GetDerivedTypes().FirstOrDefault(et => et.ClrType == type); + var derivedEntityType = StructuralType.GetDerivedTypes().FirstOrDefault(et => et.ClrType == type); return derivedEntityType == null ? QueryCompilationContext.NotTranslatedExpression - : new EntityReferenceExpression(this, derivedEntityType); + : new StructuralTypeReferenceExpression(this, derivedEntityType); } } +#endif } diff --git a/src/net/KEFCore/Query/Internal/KafkaProjectionBindingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal/KafkaProjectionBindingExpressionVisitor.cs index c56ce010..c37bc8f8 100644 --- a/src/net/KEFCore/Query/Internal/KafkaProjectionBindingExpressionVisitor.cs +++ b/src/net/KEFCore/Query/Internal/KafkaProjectionBindingExpressionVisitor.cs @@ -22,6 +22,7 @@ using System.Diagnostics.CodeAnalysis; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -42,6 +43,12 @@ public class KafkaProjectionBindingExpressionVisitor : ExpressionVisitor private List? _clientProjections; private readonly Stack _projectionMembers = new(); + /// + /// 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. + /// public KafkaProjectionBindingExpressionVisitor( KafkaQueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor, KafkaExpressionTranslatingExpressionVisitor expressionTranslatingExpressionVisitor) @@ -51,6 +58,12 @@ public KafkaProjectionBindingExpressionVisitor( _queryExpression = null!; } + /// + /// 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. + /// public virtual Expression Translate(KafkaQueryExpression queryExpression, Expression expression) { _queryExpression = queryExpression; @@ -84,6 +97,12 @@ public virtual Expression Translate(KafkaQueryExpression queryExpression, Expres return result; } + /// + /// 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. + /// [return: NotNullIfNotNull("expression")] public override Expression? Visit(Expression? expression) { @@ -91,11 +110,11 @@ public virtual Expression Translate(KafkaQueryExpression queryExpression, Expres { return null; } - - if (!(expression is NewExpression - || expression is MemberInitExpression - || expression is EntityShaperExpression - || expression is IncludeExpression)) +#if NET8_0_OR_GREATER + if (expression is not (NewExpression or MemberInitExpression or StructuralTypeShaperExpression or IncludeExpression)) +#else + if (expression is not (NewExpression or MemberInitExpression or EntityShaperExpression or IncludeExpression)) +#endif { if (_indexBasedBinding) { @@ -207,6 +226,12 @@ public virtual Expression Translate(KafkaQueryExpression queryExpression, Expres return base.Visit(expression); } + /// + /// 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. + /// protected override Expression VisitBinary(BinaryExpression binaryExpression) { var left = MatchTypes(Visit(binaryExpression.Left), binaryExpression.Left.Type); @@ -215,6 +240,12 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) return binaryExpression.Update(left, VisitAndConvert(binaryExpression.Conversion, "VisitBinary"), right); } + /// + /// 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. + /// protected override Expression VisitConditional(ConditionalExpression conditionalExpression) { var test = Visit(conditionalExpression.Test); @@ -232,12 +263,22 @@ protected override Expression VisitConditional(ConditionalExpression conditional return conditionalExpression.Update(test, ifTrue, ifFalse); } + /// + /// 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. + /// protected override Expression VisitExtension(Expression extensionExpression) { - if (extensionExpression is EntityShaperExpression entityShaperExpression) +#if NET8_0_OR_GREATER + if (extensionExpression is StructuralTypeShaperExpression shaper) +#else + if (extensionExpression is EntityShaperExpression shaper) +#endif { EntityProjectionExpression entityProjectionExpression; - if (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression) + if (shaper.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression) { entityProjectionExpression = (EntityProjectionExpression)((KafkaQueryExpression)projectionBindingExpression.QueryExpression) @@ -245,7 +286,7 @@ protected override Expression VisitExtension(Expression extensionExpression) } else { - entityProjectionExpression = (EntityProjectionExpression)entityShaperExpression.ValueBufferExpression; + entityProjectionExpression = (EntityProjectionExpression)shaper.ValueBufferExpression; } if (_indexBasedBinding) @@ -256,12 +297,12 @@ protected override Expression VisitExtension(Expression extensionExpression) _entityProjectionCache[entityProjectionExpression] = entityProjectionBinding; } - return entityShaperExpression.Update(entityProjectionBinding); + return shaper.Update(entityProjectionBinding); } _projectionMapping[_projectionMembers.Peek()] = entityProjectionExpression; - return entityShaperExpression.Update( + return shaper.Update( new ProjectionBindingExpression(_queryExpression, _projectionMembers.Peek(), typeof(ValueBuffer))); } @@ -275,9 +316,21 @@ protected override Expression VisitExtension(Expression extensionExpression) throw new InvalidOperationException(CoreStrings.TranslationFailed(extensionExpression.Print())); } + /// + /// 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. + /// protected override ElementInit VisitElementInit(ElementInit elementInit) => elementInit.Update(elementInit.Arguments.Select(e => MatchTypes(Visit(e), e.Type))); + /// + /// 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. + /// protected override Expression VisitMember(MemberExpression memberExpression) { var expression = Visit(memberExpression.Expression); @@ -301,6 +354,12 @@ protected override Expression VisitMember(MemberExpression memberExpression) return updatedMemberExpression; } + /// + /// 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. + /// protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment) { var expression = memberAssignment.Expression; @@ -328,6 +387,12 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment membe return memberAssignment.Update(visitedExpression); } + /// + /// 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. + /// protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression) { var newExpression = Visit(memberInitExpression.NewExpression); @@ -345,8 +410,7 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp } newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]); - if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert + if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression && unaryExpression.Operand == QueryCompilationContext.NotTranslatedExpression) { return QueryCompilationContext.NotTranslatedExpression; @@ -356,6 +420,12 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp return memberInitExpression.Update((NewExpression)newExpression, newBindings); } + /// + /// 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. + /// protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { var @object = Visit(methodCallExpression.Object); @@ -388,6 +458,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return updatedMethodCallExpression; } + /// + /// 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. + /// protected override Expression VisitNew(NewExpression newExpression) { if (newExpression.Arguments.Count == 0) @@ -429,15 +505,26 @@ protected override Expression VisitNew(NewExpression newExpression) return newExpression.Update(newArguments); } + /// + /// 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. + /// protected override Expression VisitNewArray(NewArrayExpression newArrayExpression) => newArrayExpression.Update(newArrayExpression.Expressions.Select(e => MatchTypes(Visit(e), e.Type))); + /// + /// 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. + /// protected override Expression VisitUnary(UnaryExpression unaryExpression) { var operand = Visit(unaryExpression.Operand); - return (unaryExpression.NodeType == ExpressionType.Convert - || unaryExpression.NodeType == ExpressionType.ConvertChecked) + return unaryExpression.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked && unaryExpression.Type == operand.Type ? operand : unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type)); diff --git a/src/net/KEFCore/Query/Internal/KafkaQueryExpression.Helper.cs b/src/net/KEFCore/Query/Internal/KafkaQueryExpression.Helper.cs index c70587b9..b08006f1 100644 --- a/src/net/KEFCore/Query/Internal/KafkaQueryExpression.Helper.cs +++ b/src/net/KEFCore/Query/Internal/KafkaQueryExpression.Helper.cs @@ -19,19 +19,11 @@ * Refer to LICENSE for more information. */ -using System; using System.Collections; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; -/// -/// 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. -/// + public partial class KafkaQueryExpression { private sealed class ResultEnumerable : IEnumerable @@ -190,8 +182,12 @@ public ProjectionIndexRemappingExpressionVisitor( private sealed class EntityShaperNullableMarkingExpressionVisitor : ExpressionVisitor { protected override Expression VisitExtension(Expression extensionExpression) - => extensionExpression is EntityShaperExpression entityShaper - ? entityShaper.MakeNullable() +#if NET8_0_OR_GREATER + => extensionExpression is StructuralTypeShaperExpression shaper +#else + => extensionExpression is EntityShaperExpression shaper +#endif + ? shaper.MakeNullable() : base.VisitExtension(extensionExpression); } diff --git a/src/net/KEFCore/Query/Internal/KafkaQueryExpression.cs b/src/net/KEFCore/Query/Internal/KafkaQueryExpression.cs index a311aa9e..f3d37d88 100644 --- a/src/net/KEFCore/Query/Internal/KafkaQueryExpression.cs +++ b/src/net/KEFCore/Query/Internal/KafkaQueryExpression.cs @@ -20,12 +20,10 @@ */ using MASES.EntityFrameworkCore.KNet.Internal; -using System; -using System.Collections.Generic; -using System.Linq; using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -41,7 +39,7 @@ private static readonly PropertyInfo ValueBufferCountMemberInfo = typeof(ValueBuffer).GetTypeInfo().GetProperty(nameof(ValueBuffer.Count))!; private static readonly MethodInfo LeftJoinMethodInfo = typeof(KafkaQueryExpression).GetTypeInfo() - .GetDeclaredMethods(nameof(LeftJoin)).Single(mi => mi.GetParameters().Length == 6); + .GetDeclaredMethods(nameof(LeftJoin)).Single(mi => mi.GetParameters().Length == 7); private static readonly ConstructorInfo ResultEnumerableConstructor = typeof(ResultEnumerable).GetConstructors().Single(); @@ -65,6 +63,12 @@ private KafkaQueryExpression( _valueBufferParameter = valueBufferParameter; } + /// + /// 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. + /// public KafkaQueryExpression(IEntityType entityType) { _valueBufferParameter = Parameter(typeof(ValueBuffer), "valueBuffer"); @@ -131,11 +135,29 @@ public KafkaQueryExpression(IEntityType entityType) _projectionMapping[new ProjectionMember()] = entityProjection; } + /// + /// 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. + /// public virtual Expression ServerQueryExpression { get; private set; } + /// + /// 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. + /// public virtual ParameterExpression CurrentParameter => _groupingParameter ?? _valueBufferParameter; + /// + /// 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. + /// public virtual void ReplaceProjection(IReadOnlyList clientProjections) { _projectionMapping.Clear(); @@ -144,6 +166,12 @@ public virtual void ReplaceProjection(IReadOnlyList clientProjection _clientProjections.AddRange(clientProjections); } + /// + /// 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. + /// public virtual void ReplaceProjection(IReadOnlyDictionary projectionMapping) { _projectionMapping.Clear(); @@ -220,11 +248,23 @@ EntityProjectionExpression AddEntityProjection(EntityProjectionExpression entity } } + /// + /// 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. + /// public virtual Expression GetProjection(ProjectionBindingExpression projectionBindingExpression) => projectionBindingExpression.ProjectionMember != null ? _projectionMapping[projectionBindingExpression.ProjectionMember] : _clientProjections[projectionBindingExpression.Index!.Value]; + /// + /// 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. + /// public virtual void ApplyProjection() { if (_scalarServerQuery) @@ -331,9 +371,21 @@ public virtual void ApplyProjection() } } + /// + /// 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. + /// public virtual void UpdateServerQueryExpression(Expression serverQueryExpression) => ServerQueryExpression = serverQueryExpression; + /// + /// 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. + /// public virtual void ApplySetOperation(MethodInfo setOperationMethodInfo, KafkaQueryExpression source2) { Check.DebugAssert(_groupingParameter == null, "Cannot apply set operation after GroupBy without flattening."); @@ -417,6 +469,12 @@ public virtual void ApplySetOperation(MethodInfo setOperationMethodInfo, KafkaQu setOperationMethodInfo.MakeGenericMethod(typeof(ValueBuffer)), ServerQueryExpression, source2.ServerQueryExpression); } + /// + /// 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. + /// public virtual void ApplyDefaultIfEmpty() { if (_clientProjections.Count != 0) @@ -444,6 +502,12 @@ public virtual void ApplyDefaultIfEmpty() Constant(new ValueBuffer(Enumerable.Repeat((object?)null, _projectionMappingExpressions.Count).ToArray()))); } + /// + /// 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. + /// public virtual void ApplyDistinct() { Check.DebugAssert(!_scalarServerQuery && _singleResultMethodInfo == null, "Cannot apply distinct on single result query"); @@ -499,6 +563,12 @@ public virtual void ApplyDistinct() selectorLambda)); } + /// + /// 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. + /// public virtual GroupByShaperExpression ApplyGrouping( Expression groupingKey, Expression shaperExpression, @@ -553,6 +623,12 @@ public virtual GroupByShaperExpression ApplyGrouping( new QueryExpressionReplacingExpressionVisitor(this, clonedKafkaQueryExpression).Visit(shaperExpression))); } + /// + /// 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. + /// public virtual Expression AddInnerJoin( KafkaQueryExpression innerQueryExpression, LambdaExpression outerKeySelector, @@ -563,6 +639,12 @@ public virtual Expression AddInnerJoin( innerQueryExpression, outerKeySelector, innerKeySelector, outerShaperExpression, innerShaperExpression, innerNullable: false); + /// + /// 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. + /// public virtual Expression AddLeftJoin( KafkaQueryExpression innerQueryExpression, LambdaExpression outerKeySelector, @@ -573,6 +655,12 @@ public virtual Expression AddLeftJoin( innerQueryExpression, outerKeySelector, innerKeySelector, outerShaperExpression, innerShaperExpression, innerNullable: true); + /// + /// 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. + /// public virtual Expression AddSelectMany( KafkaQueryExpression innerQueryExpression, Expression outerShaperExpression, @@ -580,7 +668,17 @@ public virtual Expression AddSelectMany( bool innerNullable) => AddJoin(innerQueryExpression, null, null, outerShaperExpression, innerShaperExpression, innerNullable); + /// + /// 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. + /// +#if NET8_0_OR_GREATER + public virtual StructuralTypeShaperExpression AddNavigationToWeakEntityType( +#else public virtual EntityShaperExpression AddNavigationToWeakEntityType( +#endif EntityProjectionExpression entityProjectionExpression, INavigation navigation, KafkaQueryExpression innerQueryExpression, @@ -630,16 +728,24 @@ public virtual EntityShaperExpression AddNavigationToWeakEntityType( outerKeySelector, innerKeySelector, resultSelector, - Constant( - new ValueBuffer( - Enumerable.Repeat((object?)null, selectorExpressions.Count - outerIndex).ToArray()))); - + Constant(new ValueBuffer(Enumerable.Repeat((object?)null, selectorExpressions.Count - outerIndex).ToArray())), + Constant(null, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); +#if NET8_0_OR_GREATER + var entityShaper = new StructuralTypeShaperExpression(innerEntityProjection.EntityType, innerEntityProjection, nullable: true); +#else var entityShaper = new EntityShaperExpression(innerEntityProjection.EntityType, innerEntityProjection, nullable: true); +#endif entityProjectionExpression.AddNavigationBinding(navigation, entityShaper); return entityShaper; } - /// + + /// + /// 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. + /// public virtual ShapedQueryExpression Clone(Expression shaperExpression) { var clonedKafkaQueryExpression = Clone(); @@ -649,6 +755,12 @@ public virtual ShapedQueryExpression Clone(Expression shaperExpression) new QueryExpressionReplacingExpressionVisitor(this, clonedKafkaQueryExpression).Visit(shaperExpression)); } + /// + /// 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. + /// public virtual Expression GetSingleScalarProjection() { var expression = CreateReadValueExpression(ServerQueryExpression.Type, 0, null); @@ -665,15 +777,39 @@ public virtual Expression GetSingleScalarProjection() return new ProjectionBindingExpression(this, new ProjectionMember(), expression.Type.MakeNullable()); } + /// + /// 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. + /// public virtual void ConvertToSingleResult(MethodInfo methodInfo) => _singleResultMethodInfo = methodInfo; - /// + + /// + /// 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. + /// public override Type Type => typeof(IEnumerable); - /// + + /// + /// 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. + /// public sealed override ExpressionType NodeType => ExpressionType.Extension; + /// + /// 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. + /// void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) { expressionPrinter.AppendLine(nameof(KafkaQueryExpression) + ": "); @@ -738,7 +874,7 @@ private static Expression GetGroupingKey(Expression key, List groupi return newExpression.Update(arguments); case MemberInitExpression memberInitExpression: - if (memberInitExpression.Bindings.Any(mb => !(mb is MemberAssignment))) + if (memberInitExpression.Bindings.Any(mb => mb is not MemberAssignment)) { goto default; } @@ -757,7 +893,23 @@ private static Expression GetGroupingKey(Expression key, List groupi } return memberInitExpression.Update(updatedNewExpression, memberBindings); +#if NET8_0_OR_GREATER + case StructuralTypeShaperExpression { ValueBufferExpression: ProjectionBindingExpression projectionBindingExpression } shaper: + var entityProjectionExpression = + (EntityProjectionExpression)((KafkaQueryExpression)projectionBindingExpression.QueryExpression) + .GetProjection(projectionBindingExpression); + var readExpressions = new Dictionary(); + foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) + { + readExpressions[property] = (MethodCallExpression)GetGroupingKey( + entityProjectionExpression.BindProperty(property), + groupingExpressions, + groupingKeyAccessExpression); + } + return shaper.Update( + new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressions)); +#endif default: var index = groupingExpressions.Count; groupingExpressions.Add(key); @@ -926,6 +1078,15 @@ private Expression AddJoin( if (outerKeySelector != null && innerKeySelector != null) { + var comparer = ((InferPropertyFromInner(outerKeySelector.Body) + ?? InferPropertyFromInner(outerKeySelector.Body)) + as IProperty)?.GetValueComparer(); + + if (comparer?.Type != outerKeySelector.ReturnType) + { + comparer = null; + } + if (innerNullable) { ServerQueryExpression = Call( @@ -936,20 +1097,29 @@ private Expression AddJoin( outerKeySelector, innerKeySelector, resultSelector, - Constant( - new ValueBuffer( - Enumerable.Repeat((object?)null, resultSelectorExpressions.Count - outerIndex).ToArray()))); + Constant(new ValueBuffer(Enumerable.Repeat((object?)null, resultSelectorExpressions.Count - outerIndex).ToArray())), + Constant(comparer, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); } else { - ServerQueryExpression = Call( - EnumerableMethods.Join.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), - ServerQueryExpression, - innerQueryExpression.ServerQueryExpression, - outerKeySelector, - innerKeySelector, - resultSelector); + ServerQueryExpression = comparer == null + ? Call( + EnumerableMethods.Join.MakeGenericMethod( + typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), + ServerQueryExpression, + innerQueryExpression.ServerQueryExpression, + outerKeySelector, + innerKeySelector, + resultSelector) + : Call( + EnumerableMethods.JoinWithComparer.MakeGenericMethod( + typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), + ServerQueryExpression, + innerQueryExpression.ServerQueryExpression, + outerKeySelector, + innerKeySelector, + resultSelector, + Constant(comparer, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); } } else @@ -1016,8 +1186,7 @@ private static IEnumerable GetAllPropertiesInHierarchy(IEntityType en .SelectMany(t => t.GetDeclaredProperties()); private static IPropertyBase? InferPropertyFromInner(Expression expression) - => expression is MethodCallExpression methodCallExpression - && methodCallExpression.Method.IsGenericMethod + => expression is MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression && methodCallExpression.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod ? methodCallExpression.Arguments[2].GetConstantValue() : null; @@ -1107,8 +1276,11 @@ private static IEnumerable LeftJoin( Func outerKeySelector, Func innerKeySelector, Func resultSelector, - TInner defaultValue) - => outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (oe, ies) => new { oe, ies }) + TInner defaultValue, + IEqualityComparer? comparer) + => (comparer == null + ? outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (oe, ies) => new { oe, ies }) + : outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (oe, ies) => new { oe, ies }, comparer)) .SelectMany(t => t.ies.DefaultIfEmpty(defaultValue), (t, i) => resultSelector(t.oe, i)); private static MethodCallExpression MakeReadValueNullable(Expression expression) diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryTranslationPreprocessor.cs b/src/net/KEFCore/Query/Internal/KafkaQueryTranslationPreprocessor.cs similarity index 100% rename from src/net/KEFCore/Query/Internal8/KafkaQueryTranslationPreprocessor.cs rename to src/net/KEFCore/Query/Internal/KafkaQueryTranslationPreprocessor.cs diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryTranslationPreprocessorFactory.cs b/src/net/KEFCore/Query/Internal/KafkaQueryTranslationPreprocessorFactory.cs similarity index 100% rename from src/net/KEFCore/Query/Internal8/KafkaQueryTranslationPreprocessorFactory.cs rename to src/net/KEFCore/Query/Internal/KafkaQueryTranslationPreprocessorFactory.cs diff --git a/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitor.cs index 98c1aae3..0ca04166 100644 --- a/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitor.cs @@ -19,7 +19,11 @@ * Refer to LICENSE for more information. */ +using Microsoft.EntityFrameworkCore.Query; +using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; + namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -32,7 +36,13 @@ public class KafkaQueryableMethodTranslatingExpressionVisitor : QueryableMethodT private readonly SharedTypeEntityExpandingExpressionVisitor _weakEntityExpandingExpressionVisitor; private readonly KafkaProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor; private readonly IModel _model; - /// + + /// + /// 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. + /// public KafkaQueryableMethodTranslatingExpressionVisitor( QueryableMethodTranslatingExpressionVisitorDependencies dependencies, QueryCompilationContext queryCompilationContext) @@ -43,7 +53,13 @@ public KafkaQueryableMethodTranslatingExpressionVisitor( _projectionBindingExpressionVisitor = new KafkaProjectionBindingExpressionVisitor(this, _expressionTranslator); _model = queryCompilationContext.Model; } - /// + + /// + /// 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. + /// protected KafkaQueryableMethodTranslatingExpressionVisitor( KafkaQueryableMethodTranslatingExpressionVisitor parentVisitor) : base(parentVisitor.Dependencies, parentVisitor.QueryCompilationContext, subquery: true) @@ -53,10 +69,22 @@ protected KafkaQueryableMethodTranslatingExpressionVisitor( _projectionBindingExpressionVisitor = new KafkaProjectionBindingExpressionVisitor(this, _expressionTranslator); _model = parentVisitor._model; } - /// + + /// + /// 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. + /// protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVisitor() => new KafkaQueryableMethodTranslatingExpressionVisitor(this); - /// + + /// + /// 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. + /// protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) @@ -75,7 +103,13 @@ protected override Expression VisitExtension(Expression extensionExpression) return base.VisitExtension(extensionExpression); } } - /// + + /// + /// 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. + /// protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.IsGenericMethod @@ -97,7 +131,12 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elemen throw new NotImplementedException(); } #endif - /// + /// + /// 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. + /// protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) => CreateShapedQueryExpressionStatic(entityType); @@ -107,7 +146,11 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy return new ShapedQueryExpression( queryExpression, +#if NET8_0_OR_GREATER + new StructuralTypeShaperExpression( +#else new EntityShaperExpression( +#endif entityType, new ProjectionBindingExpression( queryExpression, @@ -115,7 +158,13 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy typeof(ValueBuffer)), false)); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateAll(ShapedQueryExpression source, LambdaExpression predicate) { predicate = Expression.Lambda(Expression.Not(predicate.Body), predicate.Parameters); @@ -142,7 +191,13 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(bool))); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateAny(ShapedQueryExpression source, LambdaExpression? predicate) { if (predicate != null) @@ -170,23 +225,55 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(bool))); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateAverage( ShapedQueryExpression source, LambdaExpression? selector, Type resultType) => TranslateScalarAggregate(source, selector, nameof(Enumerable.Average), resultType); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateCast(ShapedQueryExpression source, Type resultType) => source.ShaperExpression.Type != resultType ? source.UpdateShaperExpression(Expression.Convert(source.ShaperExpression, resultType)) : source; - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2) => TranslateSetOperation(EnumerableMethods.Concat, source1, source2); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateContains(ShapedQueryExpression source, Expression item) { +#if NET7_0_OR_GREATER + var anyLambdaParameter = Expression.Parameter(item.Type, "p"); + var anyLambda = Expression.Lambda( + ExpressionExtensions.CreateEqualsExpression(anyLambdaParameter, item), + anyLambdaParameter); + + return TranslateAny(source, anyLambda); +#else var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; var newItem = TranslateExpression(item, preserveType: true); if (newItem == null) @@ -209,8 +296,15 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy item)); return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(bool))); +#endif } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateCount(ShapedQueryExpression source, LambdaExpression? predicate) { if (predicate != null) @@ -238,7 +332,13 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(int))); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateDefaultIfEmpty(ShapedQueryExpression source, Expression? defaultValue) { if (defaultValue == null) @@ -249,23 +349,47 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy return null; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateDistinct(ShapedQueryExpression source) { ((KafkaQueryExpression)source.QueryExpression).ApplyDistinct(); return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateElementAtOrDefault( ShapedQueryExpression source, Expression index, bool returnDefault) => null; - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2) => TranslateSetOperation(EnumerableMethods.Except, source1, source2); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateFirstOrDefault( ShapedQueryExpression source, LambdaExpression? predicate, @@ -279,7 +403,12 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy ? EnumerableMethods.FirstOrDefaultWithoutPredicate : EnumerableMethods.FirstWithoutPredicate); - /// + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateGroupBy( ShapedQueryExpression source, LambdaExpression keySelector, @@ -366,7 +495,10 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy } return memberInitExpression.Update(updatedNewExpression, newBindings); - +#if NET8_0_OR_GREATER + case StructuralTypeShaperExpression { ValueBufferExpression: ProjectionBindingExpression } shaper: + return shaper; +#endif default: var translation = TranslateExpression(expression); if (translation == null) @@ -379,7 +511,13 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy : Expression.Convert(translation, expression.Type); } } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateGroupJoin( ShapedQueryExpression outer, ShapedQueryExpression inner, @@ -387,10 +525,22 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy LambdaExpression innerKeySelector, LambdaExpression resultSelector) => null; - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateIntersect(ShapedQueryExpression source1, ShapedQueryExpression source2) => TranslateSetOperation(EnumerableMethods.Intersect, source1, source2); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateJoin( ShapedQueryExpression outer, ShapedQueryExpression inner, @@ -428,9 +578,11 @@ private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityTy { var left = RemapLambdaBody(outer, outerKeySelector); var right = RemapLambdaBody(inner, innerKeySelector); - +#if NET7_0_OR_GREATER + var joinCondition = TranslateExpression(ExpressionExtensions.CreateEqualsExpression(left, right)); +#else var joinCondition = TranslateExpression(Expression.Equal(left, right)); - +#endif var (outerKeyBody, innerKeyBody) = DecomposeJoinCondition(joinCondition); if (outerKeyBody == null @@ -487,11 +639,10 @@ private static bool ProcessJoinCondition( } } - if (joinCondition is MethodCallExpression methodCallExpression - && methodCallExpression.Method.IsStatic - && methodCallExpression.Method.DeclaringType == typeof(object) - && methodCallExpression.Method.Name == nameof(object.Equals) - && methodCallExpression.Arguments.Count == 2) + if (joinCondition is MethodCallExpression { Method.Name: nameof(object.Equals), Arguments.Count: 2 } methodCallExpression + && ((methodCallExpression.Method.IsStatic + && methodCallExpression.Method.DeclaringType == typeof(object)) + || typeof(ValueComparer).IsAssignableFrom(methodCallExpression.Method.DeclaringType))) { leftExpressions.Add(methodCallExpression.Arguments[0]); rightExpressions.Add(methodCallExpression.Arguments[1]); @@ -526,7 +677,13 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) && !inner.Type.IsNullableType() && outer.Type.UnwrapNullableType() == inner.Type; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateLastOrDefault( ShapedQueryExpression source, LambdaExpression? predicate, @@ -539,7 +696,13 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) returnDefault ? EnumerableMethods.LastOrDefaultWithoutPredicate : EnumerableMethods.LastWithoutPredicate); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateLeftJoin( ShapedQueryExpression outer, ShapedQueryExpression inner, @@ -568,7 +731,13 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) return TranslateTwoParameterSelector(outer, resultSelector); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateLongCount(ShapedQueryExpression source, LambdaExpression? predicate) { if (predicate != null) @@ -597,27 +766,50 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(long))); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateMax( ShapedQueryExpression source, LambdaExpression? selector, Type resultType) => TranslateScalarAggregate(source, selector, nameof(Enumerable.Max), resultType); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateMin(ShapedQueryExpression source, LambdaExpression? selector, Type resultType) => TranslateScalarAggregate(source, selector, nameof(Enumerable.Min), resultType); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateOfType(ShapedQueryExpression source, Type resultType) { - if (source.ShaperExpression is EntityShaperExpression entityShaperExpression) +#if NET8_0_OR_GREATER + if (source.ShaperExpression is StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper) { - var entityType = entityShaperExpression.EntityType; +#else + if (source.ShaperExpression is EntityShaperExpression shaper) + { + var entityType = shaper.EntityType; +#endif if (entityType.ClrType == resultType) { return source; } - var parameterExpression = Expression.Parameter(entityShaperExpression.Type); + var parameterExpression = Expression.Parameter(shaper.Type); var predicate = Expression.Lambda(Expression.TypeIs(parameterExpression, resultType), parameterExpression); var newSource = TranslateWhere(source, predicate); if (newSource == null) @@ -631,13 +823,17 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) var baseType = entityType.GetAllBaseTypes().SingleOrDefault(et => et.ClrType == resultType); if (baseType != null) { - return source.UpdateShaperExpression(entityShaperExpression.WithEntityType(baseType)); +#if NET8_0_OR_GREATER + return source.UpdateShaperExpression(shaper.WithType(baseType)); +#else + return source.UpdateShaperExpression(shaper.WithEntityType(baseType)); +#endif } var derivedType = entityType.GetDerivedTypes().Single(et => et.ClrType == resultType); var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression; + var projectionBindingExpression = (ProjectionBindingExpression)shaper.ValueBufferExpression; var projectionMember = projectionBindingExpression.ProjectionMember; Check.DebugAssert(new ProjectionMember().Equals(projectionMember), "Invalid ProjectionMember when processing OfType"); @@ -648,13 +844,22 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) { { projectionMember, entityProjectionExpression.UpdateEntityType(derivedType) } }); - - return source.UpdateShaperExpression(entityShaperExpression.WithEntityType(derivedType)); +#if NET8_0_OR_GREATER + return source.UpdateShaperExpression(shaper.WithType(derivedType)); +#else + return source.UpdateShaperExpression(shaper.WithEntityType(derivedType)); +#endif } return null; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateOrderBy( ShapedQueryExpression source, LambdaExpression keySelector, @@ -679,7 +884,13 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateReverse(ShapedQueryExpression source) { var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; @@ -691,7 +902,13 @@ static bool IsConvertedToNullable(Expression outer, Expression inner) return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression source, LambdaExpression selector) { if (selector.Body == selector.Parameters[0]) @@ -705,7 +922,13 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s return source.UpdateShaperExpression(newShaper); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSelectMany( ShapedQueryExpression source, LambdaExpression collectionSelector, @@ -751,7 +974,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return base.VisitMethodCall(methodCallExpression); } } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSelectMany(ShapedQueryExpression source, LambdaExpression selector) { var innerParameter = Expression.Parameter(selector.ReturnType.GetSequenceType(), "i"); @@ -760,7 +989,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return TranslateSelectMany(source, selector, resultSelector); } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSingleOrDefault( ShapedQueryExpression source, LambdaExpression? predicate, @@ -773,7 +1008,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp returnDefault ? EnumerableMethods.SingleOrDefaultWithoutPredicate : EnumerableMethods.SingleWithoutPredicate); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSkip(ShapedQueryExpression source, Expression count) { var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; @@ -793,13 +1034,31 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSkipWhile(ShapedQueryExpression source, LambdaExpression predicate) => null; - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateSum(ShapedQueryExpression source, LambdaExpression? selector, Type resultType) => TranslateScalarAggregate(source, selector, nameof(Enumerable.Sum), resultType); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateTake(ShapedQueryExpression source, Expression count) { var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; @@ -819,10 +1078,22 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateTakeWhile(ShapedQueryExpression source, LambdaExpression predicate) => null; - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateThenBy( ShapedQueryExpression source, LambdaExpression keySelector, @@ -846,10 +1117,22 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return source; } - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateUnion(ShapedQueryExpression source1, ShapedQueryExpression source2) => TranslateSetOperation(EnumerableMethods.Union, source1, source2); - /// + + /// + /// 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. + /// protected override ShapedQueryExpression? TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate) { var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; @@ -918,8 +1201,10 @@ private Expression ExpandSharedTypeEntities(KafkaQueryExpression queryExpression private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisitor { +#if NET6_0 private static readonly MethodInfo ObjectEqualsMethodInfo = typeof(object).GetRuntimeMethod(nameof(object.Equals), new[] { typeof(object), typeof(object) })!; +#endif private readonly KafkaExpressionTranslatingExpressionVisitor _expressionTranslator; @@ -963,20 +1248,35 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } protected override Expression VisitExtension(Expression extensionExpression) - => extensionExpression is EntityShaperExpression - || extensionExpression is ShapedQueryExpression - ? extensionExpression - : base.VisitExtension(extensionExpression); +#if NET8_0_OR_GREATER + => extensionExpression is StructuralTypeShaperExpression or ShapedQueryExpression or GroupByShaperExpression +#else + => extensionExpression is EntityShaperExpression || extensionExpression is ShapedQueryExpression +#endif + ? extensionExpression + : base.VisitExtension(extensionExpression); private Expression? TryExpand(Expression? source, MemberIdentity member) { source = source.UnwrapTypeConversion(out var convertedType); - if (source is not EntityShaperExpression entityShaperExpression) +#if NET8_0_OR_GREATER + if (source is not StructuralTypeShaperExpression shaper) +#else + if (source is not EntityShaperExpression shaper) +#endif + { + return null; + } +#if NET8_0_OR_GREATER + if (shaper.StructuralType is not IEntityType) { return null; } - var entityType = entityShaperExpression.EntityType; + var entityType = (IEntityType)shaper.StructuralType; +#else + var entityType = shaper.EntityType; +#endif if (convertedType != null) { entityType = entityType.GetRootType().GetDerivedTypesInclusive() @@ -1015,7 +1315,7 @@ protected override Expression VisitExtension(Expression extensionExpression) .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var outerKey = entityShaperExpression.CreateKeyValuesExpression( + var outerKey = shaper.CreateKeyValuesExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, @@ -1025,10 +1325,11 @@ protected override Expression VisitExtension(Expression extensionExpression) ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, makeNullable); - - var keyComparison = Expression.Call( - ObjectEqualsMethodInfo, AddConvertToObject(outerKey), AddConvertToObject(innerKey)); - +#if NET7_0_OR_GREATER + var keyComparison = ExpressionExtensions.CreateEqualsExpression(outerKey, innerKey); +#else + var keyComparison = (Expression)Expression.Call(ObjectEqualsMethodInfo, AddConvertToObject(outerKey), AddConvertToObject(innerKey)); +#endif var predicate = makeNullable ? Expression.AndAlso( outerKey is NewArrayExpression newArrayExpression @@ -1043,7 +1344,7 @@ outerKey is NewArrayExpression newArrayExpression .Aggregate((l, r) => Expression.AndAlso(l, r)) : Expression.NotEqual(outerKey, Expression.Constant(null, outerKey.Type)), keyComparison) - : (Expression)keyComparison; + : keyComparison; var correlationPredicate = _expressionTranslator.Translate(predicate)!; innerQueryExpression.UpdateServerQueryExpression( @@ -1056,9 +1357,9 @@ outerKey is NewArrayExpression newArrayExpression } var entityProjectionExpression = - entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression + shaper.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression ? (EntityProjectionExpression)_queryExpression.GetProjection(projectionBindingExpression) - : (EntityProjectionExpression)entityShaperExpression.ValueBufferExpression; + : (EntityProjectionExpression)shaper.ValueBufferExpression; var innerShaper = entityProjectionExpression.BindNavigation(navigation); if (innerShaper == null) { @@ -1070,7 +1371,7 @@ entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression proj .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var outerKey = entityShaperExpression.CreateKeyValuesExpression( + var outerKey = shaper.CreateKeyValuesExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, @@ -1097,11 +1398,12 @@ entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression proj return innerShaper; } - +#if NET6_0 private static Expression AddConvertToObject(Expression expression) => expression.Type.IsValueType ? Expression.Convert(expression, typeof(object)) : expression; +#endif } private ShapedQueryExpression TranslateTwoParameterSelector(ShapedQueryExpression source, LambdaExpression resultSelector) @@ -1224,8 +1526,13 @@ private static Expression MatchShaperNullabilityForSetOperation(Expression shape { switch (shaper1) { +#if NET8_0_OR_GREATER + case StructuralTypeShaperExpression entityShaperExpression1 + when shaper2 is StructuralTypeShaperExpression entityShaperExpression2: +#else case EntityShaperExpression entityShaperExpression1 when shaper2 is EntityShaperExpression entityShaperExpression2: +#endif return entityShaperExpression1.IsNullable != entityShaperExpression2.IsNullable ? entityShaperExpression1.MakeNullable(makeNullable) : entityShaperExpression1; diff --git a/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs index 2a7ab11f..117d5ccd 100644 --- a/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs +++ b/src/net/KEFCore/Query/Internal/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs @@ -20,6 +20,7 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -28,6 +29,12 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; /// public class KafkaQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory { + /// + /// 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. + /// public KafkaQueryableMethodTranslatingExpressionVisitorFactory( QueryableMethodTranslatingExpressionVisitorDependencies dependencies) { @@ -39,6 +46,12 @@ public KafkaQueryableMethodTranslatingExpressionVisitorFactory( /// protected virtual QueryableMethodTranslatingExpressionVisitorDependencies Dependencies { get; } + /// + /// 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. + /// public virtual QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) => new KafkaQueryableMethodTranslatingExpressionVisitor(Dependencies, queryCompilationContext); } diff --git a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs index 4698bc7b..bf2706e0 100644 --- a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs +++ b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs @@ -19,10 +19,11 @@ * Refer to LICENSE for more information. */ -using System.Collections; using MASES.EntityFrameworkCore.KNet.Internal; +using System.Collections; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -80,7 +81,9 @@ private sealed class Enumerator : IEnumerator, IAsyncEnumerator private readonly bool _standAloneStateManager; private readonly CancellationToken _cancellationToken; private readonly IConcurrencyDetector? _concurrencyDetector; - +#if NET7_0_OR_GREATER + private readonly IExceptionDetector _exceptionDetector; +#endif private IEnumerator? _enumerator; public Enumerator(QueryingEnumerable queryingEnumerable, CancellationToken cancellationToken = default) @@ -92,6 +95,9 @@ public Enumerator(QueryingEnumerable queryingEnumerable, CancellationToken ca _queryLogger = queryingEnumerable._queryLogger; _standAloneStateManager = queryingEnumerable._standAloneStateManager; _cancellationToken = cancellationToken; +#if NET7_0_OR_GREATER + _exceptionDetector = _queryContext.ExceptionDetector; +#endif Current = default!; _concurrencyDetector = queryingEnumerable._threadSafetyChecksEnabled @@ -121,7 +127,18 @@ public bool MoveNext() } catch (Exception exception) { +#if NET7_0_OR_GREATER + if (_exceptionDetector.IsCancellation(exception)) + { + _queryLogger.QueryCanceled(_contextType); + } + else + { + _queryLogger.QueryIterationFailed(_contextType, exception); + } +#else _queryLogger.QueryIterationFailed(_contextType, exception); +#endif throw; } @@ -137,7 +154,7 @@ public ValueTask MoveNextAsync() { _cancellationToken.ThrowIfCancellationRequested(); - return new ValueTask(MoveNextHelper()); + return ValueTask.FromResult(MoveNextHelper()); } finally { @@ -146,8 +163,18 @@ public ValueTask MoveNextAsync() } catch (Exception exception) { +#if NET7_0_OR_GREATER + if (_exceptionDetector.IsCancellation(exception, _cancellationToken)) + { + _queryLogger.QueryCanceled(_contextType); + } + else + { + _queryLogger.QueryIterationFailed(_contextType, exception); + } +#else _queryLogger.QueryIterationFailed(_contextType, exception); - +#endif throw; } } diff --git a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs index a56dc93f..b708685c 100644 --- a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs +++ b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs @@ -19,15 +19,11 @@ * Refer to LICENSE for more information. */ +using static System.Linq.Expressions.Expression; using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; namespace MASES.EntityFrameworkCore.KNet.Query.Internal; -/// -/// 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. -/// + public partial class KafkaShapedQueryCompilingExpressionVisitor { private sealed class ShaperExpressionProcessingExpressionVisitor : ExpressionVisitor @@ -78,29 +74,37 @@ public LambdaExpression ProcessShaper(Expression shaperExpression) { var result = Visit(shaperExpression); _expressions.Add(result); - result = Expression.Block(_variables, _expressions); + result = Block(_variables, _expressions); // If parameter is null then the projection is not really server correlated so we can just put anything. - _valueBufferParameter ??= Expression.Parameter(typeof(ValueBuffer)); + _valueBufferParameter ??= Parameter(typeof(ValueBuffer)); - return Expression.Lambda(result, QueryCompilationContext.QueryContextParameter, _valueBufferParameter); + return Lambda(result, QueryCompilationContext.QueryContextParameter, _valueBufferParameter); } protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) { - case EntityShaperExpression entityShaperExpression: +#if NET8_0_OR_GREATER + case StructuralTypeShaperExpression shaper: +#else + case EntityShaperExpression shaper: +#endif { - var key = entityShaperExpression.ValueBufferExpression; + var key = shaper.ValueBufferExpression; if (!_mapping.TryGetValue(key, out var variable)) { - variable = Expression.Parameter(entityShaperExpression.EntityType.ClrType); +#if NET8_0_OR_GREATER + variable = Parameter(shaper.StructuralType.ClrType); +#else + variable = Parameter(shaper.EntityType.ClrType); +#endif _variables.Add(variable); var innerShaper = - _kafkaShapedQueryCompilingExpressionVisitor.InjectEntityMaterializers(entityShaperExpression); + _kafkaShapedQueryCompilingExpressionVisitor.InjectEntityMaterializers(shaper); innerShaper = Visit(innerShaper); - _expressions.Add(Expression.Assign(variable, innerShaper)); + _expressions.Add(Assign(variable, innerShaper)); _mapping[key] = variable; } @@ -112,7 +116,7 @@ protected override Expression VisitExtension(Expression extensionExpression) var key = projectionBindingExpression; if (!_mapping.TryGetValue(key, out var variable)) { - variable = Expression.Parameter(projectionBindingExpression.Type); + variable = Parameter(projectionBindingExpression.Type); _variables.Add(variable); var queryExpression = (KafkaQueryExpression)projectionBindingExpression.QueryExpression; _valueBufferParameter ??= queryExpression.CurrentParameter; @@ -121,7 +125,7 @@ protected override Expression VisitExtension(Expression extensionExpression) // We don't need to pass property when reading at top-level _expressions.Add( - Expression.Assign( + Assign( variable, queryExpression.CurrentParameter.CreateValueBufferReadValueExpression( projectionBindingExpression.Type, projectionIndex, property: null))); _mapping[key] = variable; @@ -150,38 +154,38 @@ protected override Expression VisitExtension(Expression extensionExpression) _kafkaShapedQueryCompilingExpressionVisitor, _tracking) .ProcessShaper(collectionResultShaperExpression.InnerShaper); _expressions.Add( - Expression.Call( + Call( IncludeCollectionMethodInfo.MakeGenericMethod(entityClrType, includingClrType, relatedEntityClrType), QueryCompilationContext.QueryContextParameter, Visit(collectionResultShaperExpression.Projection), - Expression.Constant(shaperLambda.Compile()), + Constant(shaperLambda.Compile()), entity, - Expression.Constant(includeExpression.Navigation), - Expression.Constant(inverseNavigation, typeof(INavigationBase)), - Expression.Constant( + Constant(includeExpression.Navigation), + Constant(inverseNavigation, typeof(INavigationBase)), + Constant( GenerateFixup( includingClrType, relatedEntityClrType, includeExpression.Navigation, inverseNavigation) .Compile()), - Expression.Constant(_tracking), + Constant(_tracking), #pragma warning disable EF1001 // Internal EF Core API usage. - Expression.Constant(includeExpression.SetLoaded))); + Constant(includeExpression.SetLoaded))); #pragma warning restore EF1001 // Internal EF Core API usage. - } + } else { _expressions.Add( - Expression.Call( + Call( IncludeReferenceMethodInfo.MakeGenericMethod(entityClrType, includingClrType, relatedEntityClrType), QueryCompilationContext.QueryContextParameter, entity, Visit(includeExpression.NavigationExpression), - Expression.Constant(includeExpression.Navigation), - Expression.Constant(inverseNavigation, typeof(INavigationBase)), - Expression.Constant( + Constant(includeExpression.Navigation), + Constant(inverseNavigation, typeof(INavigationBase)), + Constant( GenerateFixup( includingClrType, relatedEntityClrType, includeExpression.Navigation, inverseNavigation) .Compile()), - Expression.Constant(_tracking))); + Constant(_tracking))); } return entity; @@ -197,12 +201,12 @@ protected override Expression VisitExtension(Expression extensionExpression) _kafkaShapedQueryCompilingExpressionVisitor, _tracking) .ProcessShaper(collectionResultShaperExpression.InnerShaper); - return Expression.Call( + return Call( MaterializeCollectionMethodInfo.MakeGenericMethod(elementType, collectionType), QueryCompilationContext.QueryContextParameter, Visit(collectionResultShaperExpression.Projection), - Expression.Constant(shaperLambda.Compile()), - Expression.Constant(collectionAccessor, typeof(IClrCollectionAccessor))); + Constant(shaperLambda.Compile()), + Constant(collectionAccessor, typeof(IClrCollectionAccessor))); } case SingleResultShaperExpression singleResultShaperExpression: @@ -211,11 +215,11 @@ protected override Expression VisitExtension(Expression extensionExpression) _kafkaShapedQueryCompilingExpressionVisitor, _tracking) .ProcessShaper(singleResultShaperExpression.InnerShaper); - return Expression.Call( + return Call( MaterializeSingleResultMethodInfo.MakeGenericMethod(singleResultShaperExpression.Type), QueryCompilationContext.QueryContextParameter, Visit(singleResultShaperExpression.Projection), - Expression.Constant(shaperLambda.Compile())); + Constant(shaperLambda.Compile())); } } @@ -224,8 +228,7 @@ protected override Expression VisitExtension(Expression extensionExpression) protected override Expression VisitBinary(BinaryExpression binaryExpression) { - if (binaryExpression.NodeType == ExpressionType.Assign - && binaryExpression.Left is ParameterExpression parameterExpression + if (binaryExpression is { NodeType: ExpressionType.Assign, Left: ParameterExpression parameterExpression } && parameterExpression.Type == typeof(MaterializationContext)) { var newExpression = (NewExpression)binaryExpression.Right; @@ -238,15 +241,13 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) = queryExpression.GetProjection(projectionBindingExpression).GetConstantValue>(); var updatedExpression = newExpression.Update( - new[] { Expression.Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); + new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); - return Expression.MakeBinary(ExpressionType.Assign, binaryExpression.Left, updatedExpression); + return MakeBinary(ExpressionType.Assign, binaryExpression.Left, updatedExpression); } - if (binaryExpression.NodeType == ExpressionType.Assign - && binaryExpression.Left is MemberExpression memberExpression - && memberExpression.Member is FieldInfo fieldInfo - && fieldInfo.IsInitOnly) + if (binaryExpression is + { NodeType: ExpressionType.Assign, Left: MemberExpression { Member: FieldInfo { IsInitOnly: true } } memberExpression }) { return memberExpression.Assign(Visit(binaryExpression.Right)); } @@ -266,10 +267,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp Check.DebugAssert( property != null || methodCallExpression.Type.IsNullableType(), "Must read nullable value without property"); - return Expression.Call( + return Call( methodCallExpression.Method, _valueBufferParameter!, - Expression.Constant(indexMap[property!]), + Constant(indexMap[property!]), methodCallExpression.Arguments[2]); } @@ -279,9 +280,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp private static void IncludeReference( QueryContext queryContext, TEntity entity, - TIncludedEntity relatedEntity, + TIncludedEntity? relatedEntity, INavigationBase navigation, - INavigationBase inverseNavigation, + INavigationBase? inverseNavigation, Action fixup, bool trackingQuery) where TIncludingEntity : class, TEntity @@ -305,8 +306,7 @@ private static void IncludeReference if (relatedEntity != null) { fixup(includingEntity, relatedEntity); - if (inverseNavigation != null - && !inverseNavigation.IsCollection) + if (inverseNavigation is { IsCollection: false }) { inverseNavigation.SetIsLoadedWhenNoTracking(relatedEntity); } @@ -321,7 +321,7 @@ private static void IncludeCollection innerShaper, TEntity entity, INavigationBase navigation, - INavigationBase inverseNavigation, + INavigationBase? inverseNavigation, Action fixup, bool trackingQuery, bool setLoaded) @@ -331,8 +331,10 @@ private static void IncludeCollection + var entityParameter = Parameter(entityType); + var relatedEntityParameter = Parameter(relatedEntityType); + var expressions = new List(); + + if (!navigation.IsShadowProperty()) { - navigation.IsCollection - ? AddToCollectionNavigation(entityParameter, relatedEntityParameter, navigation) - : AssignReferenceNavigation(entityParameter, relatedEntityParameter, navigation) - }; + expressions.Add( + navigation.IsCollection + ? AddToCollectionNavigation(entityParameter, relatedEntityParameter, navigation) + : AssignReferenceNavigation(entityParameter, relatedEntityParameter, navigation)); + } - if (inverseNavigation != null) + if (inverseNavigation != null + && !inverseNavigation.IsShadowProperty()) { expressions.Add( inverseNavigation.IsCollection @@ -408,7 +414,7 @@ private static LambdaExpression GenerateFixup( : AssignReferenceNavigation(relatedEntityParameter, entityParameter, inverseNavigation)); } - return Expression.Lambda(Expression.Block(typeof(void), expressions), entityParameter, relatedEntityParameter); + return Lambda(Block(typeof(void), expressions), entityParameter, relatedEntityParameter); } private static Expression AssignReferenceNavigation( @@ -421,11 +427,11 @@ private static Expression AddToCollectionNavigation( ParameterExpression entity, ParameterExpression relatedEntity, INavigationBase navigation) - => Expression.Call( - Expression.Constant(navigation.GetCollectionAccessor()), + => Call( + Constant(navigation.GetCollectionAccessor()), CollectionAccessorAddMethodInfo, entity, relatedEntity, - Expression.Constant(true)); + Constant(true)); } } diff --git a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.cs index f546d5a2..f862d19d 100644 --- a/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.cs +++ b/src/net/KEFCore/Query/Internal/KafkaShapedQueryCompilingExpressionVisitor.cs @@ -20,18 +20,19 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; -/// -/// 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. -/// + +using static Expression; + public partial class KafkaShapedQueryCompilingExpressionVisitor : ShapedQueryCompilingExpressionVisitor { private readonly Type _contextType; private readonly bool _threadSafetyChecksEnabled; + /// - /// Default initilizer + /// 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. /// public KafkaShapedQueryCompilingExpressionVisitor( ShapedQueryCompilingExpressionVisitorDependencies dependencies, @@ -41,21 +42,33 @@ public KafkaShapedQueryCompilingExpressionVisitor( _contextType = queryCompilationContext.ContextType; _threadSafetyChecksEnabled = dependencies.CoreSingletonOptions.AreThreadSafetyChecksEnabled; } - /// + + /// + /// 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. + /// protected override Expression VisitExtension(Expression extensionExpression) { switch (extensionExpression) { case KafkaTableExpression kafkaTableExpression: - return Expression.Call( + return Call( TableMethodInfo, QueryCompilationContext.QueryContextParameter, - Expression.Constant(kafkaTableExpression.EntityType)); + Constant(kafkaTableExpression.EntityType)); } return base.VisitExtension(extensionExpression); } - /// + + /// + /// 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. + /// protected override Expression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression) { var kafkaQueryExpression = (KafkaQueryExpression)shapedQueryExpression.QueryExpression; @@ -66,15 +79,15 @@ protected override Expression VisitShapedQuery(ShapedQueryExpression shapedQuery .ProcessShaper(shapedQueryExpression.ShaperExpression); var innerEnumerable = Visit(kafkaQueryExpression.ServerQueryExpression); - return Expression.New( + return New( typeof(QueryingEnumerable<>).MakeGenericType(shaperExpression.ReturnType).GetConstructors()[0], QueryCompilationContext.QueryContextParameter, innerEnumerable, - Expression.Constant(shaperExpression.Compile()), - Expression.Constant(_contextType), - Expression.Constant( + Constant(shaperExpression.Compile()), + Constant(_contextType), + Constant( QueryCompilationContext.QueryTrackingBehavior == QueryTrackingBehavior.NoTrackingWithIdentityResolution), - Expression.Constant(_threadSafetyChecksEnabled)); + Constant(_threadSafetyChecksEnabled)); } private static readonly MethodInfo TableMethodInfo diff --git a/src/net/KEFCore/Query/Internal/KafkaShapedQueryExpressionVisitorFactory.cs b/src/net/KEFCore/Query/Internal/KafkaShapedQueryExpressionVisitorFactory.cs index 7740210c..10f850eb 100644 --- a/src/net/KEFCore/Query/Internal/KafkaShapedQueryExpressionVisitorFactory.cs +++ b/src/net/KEFCore/Query/Internal/KafkaShapedQueryExpressionVisitorFactory.cs @@ -20,6 +20,7 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -29,7 +30,10 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; public class KafkaShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory { /// - /// Default initializer + /// 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. /// public KafkaShapedQueryCompilingExpressionVisitorFactory( ShapedQueryCompilingExpressionVisitorDependencies dependencies) @@ -41,7 +45,13 @@ public KafkaShapedQueryCompilingExpressionVisitorFactory( /// Dependencies for this service. /// protected virtual ShapedQueryCompilingExpressionVisitorDependencies Dependencies { get; } - /// + + /// + /// 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. + /// public virtual ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) => new KafkaShapedQueryCompilingExpressionVisitor(Dependencies, queryCompilationContext); } diff --git a/src/net/KEFCore/Query/Internal/KafkaTableExpression.cs b/src/net/KEFCore/Query/Internal/KafkaTableExpression.cs index 18408629..26885d4e 100644 --- a/src/net/KEFCore/Query/Internal/KafkaTableExpression.cs +++ b/src/net/KEFCore/Query/Internal/KafkaTableExpression.cs @@ -20,6 +20,7 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -29,26 +30,57 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; public class KafkaTableExpression : Expression, IPrintableExpression { /// - /// Default initializer + /// 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. /// public KafkaTableExpression(IEntityType entityType) { EntityType = entityType; } - /// + + /// + /// 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. + /// public override Type Type => typeof(IEnumerable); + /// - /// associated to the + /// 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. /// public virtual IEntityType EntityType { get; } - /// + + /// + /// 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. + /// public sealed override ExpressionType NodeType => ExpressionType.Extension; - /// + + /// + /// 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. + /// protected override Expression VisitChildren(ExpressionVisitor visitor) => this; + /// + /// 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. + /// void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) => expressionPrinter.Append(nameof(KafkaTableExpression) + ": Entity: " + EntityType.DisplayName()); } diff --git a/src/net/KEFCore/Query/Internal/SingleResultShaperExpression.cs b/src/net/KEFCore/Query/Internal/SingleResultShaperExpression.cs index d2273933..7c01d133 100644 --- a/src/net/KEFCore/Query/Internal/SingleResultShaperExpression.cs +++ b/src/net/KEFCore/Query/Internal/SingleResultShaperExpression.cs @@ -20,6 +20,7 @@ */ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; + /// /// 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 @@ -29,7 +30,10 @@ namespace MASES.EntityFrameworkCore.KNet.Query.Internal; public class SingleResultShaperExpression : Expression, IPrintableExpression { /// - /// Default initializer + /// 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. /// public SingleResultShaperExpression( Expression projection, @@ -39,7 +43,13 @@ public SingleResultShaperExpression( InnerShaper = innerShaper; Type = innerShaper.Type; } - /// + + /// + /// 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. + /// protected override Expression VisitChildren(ExpressionVisitor visitor) { var projection = visitor.Visit(Projection); @@ -48,24 +58,56 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return Update(projection, innerShaper); } + /// + /// 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. + /// public virtual SingleResultShaperExpression Update(Expression projection, Expression innerShaper) => projection != Projection || innerShaper != InnerShaper ? new SingleResultShaperExpression(projection, innerShaper) : this; - /// + + /// + /// 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. + /// public sealed override ExpressionType NodeType => ExpressionType.Extension; - /// + + /// + /// 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. + /// public override Type Type { get; } + /// - /// Projection + /// 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. /// public virtual Expression Projection { get; } + /// - /// Inner shaper + /// 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. /// public virtual Expression InnerShaper { get; } + /// + /// 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. + /// void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) { expressionPrinter.AppendLine($"{nameof(SingleResultShaperExpression)}:"); diff --git a/src/net/KEFCore/Query/Internal8/AnonymousObject.cs b/src/net/KEFCore/Query/Internal8/AnonymousObject.cs deleted file mode 100644 index 2eafcefe..00000000 --- a/src/net/KEFCore/Query/Internal8/AnonymousObject.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using JetBrains.Annotations; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public readonly struct AnonymousObject -{ - private readonly object[] _values; - - /// - /// 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. - /// - public static readonly ConstructorInfo AnonymousObjectCtor - = typeof(AnonymousObject).GetTypeInfo() - .DeclaredConstructors - .Single(c => c.GetParameters().Length == 1); - - /// - /// 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. - /// - [UsedImplicitly] - public AnonymousObject(object[] values) - { - _values = values; - } - - /// - /// 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. - /// - public static bool operator ==(AnonymousObject x, AnonymousObject y) - => x.Equals(y); - - /// - /// 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. - /// - public static bool operator !=(AnonymousObject x, AnonymousObject y) - => !x.Equals(y); - - /// - /// 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. - /// - public override bool Equals(object? obj) - => obj is not null - && (obj is AnonymousObject anonymousObject - && _values.SequenceEqual(anonymousObject._values)); - - /// - /// 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. - /// - public override int GetHashCode() - { - var hash = new HashCode(); - foreach (var value in _values) - { - hash.Add(value); - } - - return hash.ToHashCode(); - } -} diff --git a/src/net/KEFCore/Query/Internal8/CollectionResultShaperExpression.cs b/src/net/KEFCore/Query/Internal8/CollectionResultShaperExpression.cs deleted file mode 100644 index a9e9ec92..00000000 --- a/src/net/KEFCore/Query/Internal8/CollectionResultShaperExpression.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class CollectionResultShaperExpression : Expression, IPrintableExpression -{ - /// - /// 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. - /// - public CollectionResultShaperExpression( - Expression projection, - Expression innerShaper, - INavigationBase? navigation, - Type elementType) - { - Projection = projection; - InnerShaper = innerShaper; - Navigation = navigation; - ElementType = elementType; - } - - /// - /// 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. - /// - public virtual Expression Projection { get; } - - /// - /// 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. - /// - public virtual Expression InnerShaper { get; } - - /// - /// 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. - /// - public virtual INavigationBase? Navigation { get; } - - /// - /// 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. - /// - public virtual Type ElementType { get; } - - /// - public sealed override ExpressionType NodeType - => ExpressionType.Extension; - - /// - public override Type Type - => Navigation?.ClrType ?? typeof(List<>).MakeGenericType(ElementType); - - /// - protected override Expression VisitChildren(ExpressionVisitor visitor) - { - var projection = visitor.Visit(Projection); - var innerShaper = visitor.Visit(InnerShaper); - - return Update(projection, innerShaper); - } - - /// - /// 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. - /// - public virtual CollectionResultShaperExpression Update( - Expression projection, - Expression innerShaper) - => projection != Projection || innerShaper != InnerShaper - ? new CollectionResultShaperExpression(projection, innerShaper, Navigation, ElementType) - : this; - - /// - void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) - { - expressionPrinter.AppendLine("CollectionResultShaperExpression:"); - using (expressionPrinter.Indent()) - { - expressionPrinter.Append("("); - expressionPrinter.Visit(Projection); - expressionPrinter.Append(", "); - expressionPrinter.Visit(InnerShaper); - expressionPrinter.AppendLine($", {Navigation?.Name}, {ElementType.ShortDisplayName()})"); - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/EntityProjectionExpression.cs b/src/net/KEFCore/Query/Internal8/EntityProjectionExpression.cs deleted file mode 100644 index 53821020..00000000 --- a/src/net/KEFCore/Query/Internal8/EntityProjectionExpression.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using MASES.EntityFrameworkCore.KNet.Internal; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class EntityProjectionExpression : Expression, IPrintableExpression -{ - private readonly IReadOnlyDictionary _readExpressionMap; - private readonly Dictionary _navigationExpressionsCache = new(); - - /// - /// 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. - /// - public EntityProjectionExpression( - IEntityType entityType, - IReadOnlyDictionary readExpressionMap) - { - EntityType = entityType; - _readExpressionMap = readExpressionMap; - } - - /// - /// 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. - /// - public virtual IEntityType EntityType { get; } - - /// - /// 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. - /// - public override Type Type - => EntityType.ClrType; - - /// - /// 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. - /// - public sealed override ExpressionType NodeType - => ExpressionType.Extension; - - /// - /// 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. - /// - public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedType) - { - if (!derivedType.GetAllBaseTypes().Contains(EntityType)) - { - throw new InvalidOperationException( - KafkaStrings.InvalidDerivedTypeInEntityProjection( - derivedType.DisplayName(), EntityType.DisplayName())); - } - - var readExpressionMap = new Dictionary(); - foreach (var (property, methodCallExpression) in _readExpressionMap) - { - if (derivedType.IsAssignableFrom(property.DeclaringType) - || property.DeclaringType.IsAssignableFrom(derivedType)) - { - readExpressionMap[property] = methodCallExpression; - } - } - - return new EntityProjectionExpression(derivedType, readExpressionMap); - } - - /// - /// 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. - /// - public virtual MethodCallExpression BindProperty(IProperty property) - { - if (property.DeclaringType is not IEntityType entityType) - { - if (EntityType != property.DeclaringType) - { - throw new InvalidOperationException( - KafkaStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName())); - } - } - else if (!EntityType.IsAssignableFrom(entityType) - && !entityType.IsAssignableFrom(EntityType)) - { - throw new InvalidOperationException( - KafkaStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName())); - } - - return _readExpressionMap[property]; - } - - /// - /// 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. - /// - public virtual void AddNavigationBinding(INavigation navigation, StructuralTypeShaperExpression shaper) - { - if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) - && !navigation.DeclaringEntityType.IsAssignableFrom(EntityType)) - { - throw new InvalidOperationException( - KafkaStrings.UnableToBindMemberToEntityProjection("navigation", navigation.Name, EntityType.DisplayName())); - } - - _navigationExpressionsCache[navigation] = shaper; - } - - /// - /// 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. - /// - public virtual StructuralTypeShaperExpression? BindNavigation(INavigation navigation) - { - if (!EntityType.IsAssignableFrom(navigation.DeclaringEntityType) - && !navigation.DeclaringEntityType.IsAssignableFrom(EntityType)) - { - throw new InvalidOperationException( - KafkaStrings.UnableToBindMemberToEntityProjection("navigation", navigation.Name, EntityType.DisplayName())); - } - - return _navigationExpressionsCache.TryGetValue(navigation, out var expression) - ? expression - : null; - } - - /// - /// 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. - /// - public virtual EntityProjectionExpression Clone() - { - var readExpressionMap = new Dictionary(_readExpressionMap); - var entityProjectionExpression = new EntityProjectionExpression(EntityType, readExpressionMap); - foreach (var (navigation, entityShaperExpression) in _navigationExpressionsCache) - { - entityProjectionExpression._navigationExpressionsCache[navigation] = new StructuralTypeShaperExpression( - entityShaperExpression.StructuralType, - ((EntityProjectionExpression)entityShaperExpression.ValueBufferExpression).Clone(), - entityShaperExpression.IsNullable); - } - - return entityProjectionExpression; - } - - /// - /// 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. - /// - void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) - { - expressionPrinter.AppendLine(nameof(EntityProjectionExpression) + ":"); - using (expressionPrinter.Indent()) - { - foreach (var (property, methodCallExpression) in _readExpressionMap) - { - expressionPrinter.Append(property + " -> "); - expressionPrinter.Visit(methodCallExpression); - expressionPrinter.AppendLine(); - } - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaExpressionTranslatingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal8/KafkaExpressionTranslatingExpressionVisitor.cs deleted file mode 100644 index a3760ac3..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaExpressionTranslatingExpressionVisitor.cs +++ /dev/null @@ -1,1725 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Text.RegularExpressions; -using JetBrains.Annotations; -using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaExpressionTranslatingExpressionVisitor : ExpressionVisitor -{ - private const string RuntimeParameterPrefix = QueryCompilationContext.QueryParameterPrefix + "entity_equality_"; - - private static readonly List SingleResultMethodInfos = new() - { - QueryableMethods.FirstWithPredicate, - QueryableMethods.FirstWithoutPredicate, - QueryableMethods.FirstOrDefaultWithPredicate, - QueryableMethods.FirstOrDefaultWithoutPredicate, - QueryableMethods.SingleWithPredicate, - QueryableMethods.SingleWithoutPredicate, - QueryableMethods.SingleOrDefaultWithPredicate, - QueryableMethods.SingleOrDefaultWithoutPredicate, - QueryableMethods.LastWithPredicate, - QueryableMethods.LastWithoutPredicate, - QueryableMethods.LastOrDefaultWithPredicate, - QueryableMethods.LastOrDefaultWithoutPredicate - //QueryableMethodProvider.ElementAtMethodInfo, - //QueryableMethodProvider.ElementAtOrDefaultMethodInfo - }; - - private static readonly MemberInfo ValueBufferIsEmpty = typeof(ValueBuffer).GetMember(nameof(ValueBuffer.IsEmpty))[0]; - - private static readonly MethodInfo ParameterValueExtractorMethod = - typeof(KafkaExpressionTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ParameterValueExtractor))!; - - private static readonly MethodInfo ParameterListValueExtractorMethod = - typeof(KafkaExpressionTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ParameterListValueExtractor))!; - - private static readonly MethodInfo GetParameterValueMethodInfo = - typeof(KafkaExpressionTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(GetParameterValue))!; - - private static readonly MethodInfo LikeMethodInfo = typeof(DbFunctionsExtensions).GetRuntimeMethod( - nameof(DbFunctionsExtensions.Like), new[] { typeof(DbFunctions), typeof(string), typeof(string) })!; - - private static readonly MethodInfo LikeMethodInfoWithEscape = typeof(DbFunctionsExtensions).GetRuntimeMethod( - nameof(DbFunctionsExtensions.Like), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(string) })!; - - private static readonly MethodInfo RandomMethodInfo = typeof(DbFunctionsExtensions).GetRuntimeMethod( - nameof(DbFunctionsExtensions.Random), new[] { typeof(DbFunctions) })!; - - private static readonly MethodInfo RandomNextDoubleMethodInfo = typeof(Random).GetRuntimeMethod( - nameof(Random.NextDouble), Type.EmptyTypes)!; - - private static readonly MethodInfo KafkaLikeMethodInfo = - typeof(KafkaExpressionTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(KafkaLike))!; - - private static readonly MethodInfo GetTypeMethodInfo = typeof(object).GetTypeInfo().GetDeclaredMethod(nameof(GetType))!; - - // Regex special chars defined here: - // https://msdn.microsoft.com/en-us/library/4edbef7e(v=vs.110).aspx - private static readonly char[] RegexSpecialChars - = { '.', '$', '^', '{', '[', '(', '|', ')', '*', '+', '?', '\\' }; - - private static readonly string DefaultEscapeRegexCharsPattern = BuildEscapeRegexCharsPattern(RegexSpecialChars); - - private static readonly TimeSpan RegexTimeout = TimeSpan.FromMilliseconds(value: 1000.0); - - private static string BuildEscapeRegexCharsPattern(IEnumerable regexSpecialChars) - => string.Join("|", regexSpecialChars.Select(c => @"\" + c)); - - private readonly QueryCompilationContext _queryCompilationContext; - private readonly QueryableMethodTranslatingExpressionVisitor _queryableMethodTranslatingExpressionVisitor; - private readonly EntityReferenceFindingExpressionVisitor _entityReferenceFindingExpressionVisitor; - private readonly IModel _model; - - /// - /// 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. - /// - public KafkaExpressionTranslatingExpressionVisitor( - QueryCompilationContext queryCompilationContext, - QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor) - { - _queryCompilationContext = queryCompilationContext; - _queryableMethodTranslatingExpressionVisitor = queryableMethodTranslatingExpressionVisitor; - _entityReferenceFindingExpressionVisitor = new EntityReferenceFindingExpressionVisitor(); - _model = queryCompilationContext.Model; - } - - /// - /// 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. - /// - public virtual string? TranslationErrorDetails { get; private set; } - - /// - /// 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. - /// - protected virtual void AddTranslationErrorDetails(string details) - { - if (TranslationErrorDetails == null) - { - TranslationErrorDetails = details; - } - else - { - TranslationErrorDetails += Environment.NewLine + details; - } - } - - /// - /// 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. - /// - public virtual Expression? Translate(Expression expression) - { - TranslationErrorDetails = null; - - return TranslateInternal(expression); - } - - private Expression? TranslateInternal(Expression expression) - { - var result = Visit(expression); - - return result == QueryCompilationContext.NotTranslatedExpression - || _entityReferenceFindingExpressionVisitor.Find(result) - ? null - : result; - } - - /// - /// 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. - /// - protected override Expression VisitBinary(BinaryExpression binaryExpression) - { - if (binaryExpression.Left.Type == typeof(object[]) - && binaryExpression is { Left: NewArrayExpression, NodeType: ExpressionType.Equal }) - { - return Visit(ConvertObjectArrayEqualityComparison(binaryExpression.Left, binaryExpression.Right)); - } - - if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual - && (binaryExpression.Left.IsNullConstantExpression() || binaryExpression.Right.IsNullConstantExpression())) - { - var nonNullExpression = binaryExpression.Left.IsNullConstantExpression() ? binaryExpression.Right : binaryExpression.Left; - if (nonNullExpression is MethodCallExpression nonNullMethodCallExpression - && nonNullMethodCallExpression.Method.DeclaringType == typeof(Queryable) - && nonNullMethodCallExpression.Method.IsGenericMethod - && SingleResultMethodInfos.Contains(nonNullMethodCallExpression.Method.GetGenericMethodDefinition())) - { - var source = nonNullMethodCallExpression.Arguments[0]; - if (nonNullMethodCallExpression.Arguments.Count == 2) - { - source = Expression.Call( - QueryableMethods.Where.MakeGenericMethod(source.Type.GetSequenceType()), - source, - nonNullMethodCallExpression.Arguments[1]); - } - - var translatedSubquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(source); - if (translatedSubquery != null) - { - var projection = translatedSubquery.ShaperExpression; - if (projection is NewExpression - || RemoveConvert(projection) is StructuralTypeShaperExpression { IsNullable: false } - || RemoveConvert(projection) is CollectionResultShaperExpression) - { - var anySubquery = Expression.Call( - QueryableMethods.AnyWithoutPredicate.MakeGenericMethod(translatedSubquery.Type.GetSequenceType()), - translatedSubquery); - - return Visit( - binaryExpression.NodeType == ExpressionType.Equal - ? Expression.Not(anySubquery) - : anySubquery); - } - - static Expression RemoveConvert(Expression e) - => e is UnaryExpression { NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked } unary - ? RemoveConvert(unary.Operand) - : e; - } - } - } - - if (binaryExpression.NodeType == ExpressionType.Equal - || binaryExpression.NodeType == ExpressionType.NotEqual - && binaryExpression.Left.Type == typeof(Type)) - { - if (IsGetTypeMethodCall(binaryExpression.Left, out var entityReference1) - && IsTypeConstant(binaryExpression.Right, out var type1)) - { - return ProcessGetType(entityReference1!, type1!, binaryExpression.NodeType == ExpressionType.Equal); - } - - if (IsGetTypeMethodCall(binaryExpression.Right, out var entityReference2) - && IsTypeConstant(binaryExpression.Left, out var type2)) - { - return ProcessGetType(entityReference2!, type2!, binaryExpression.NodeType == ExpressionType.Equal); - } - } - - var newLeft = Visit(binaryExpression.Left); - var newRight = Visit(binaryExpression.Right); - - if (newLeft == QueryCompilationContext.NotTranslatedExpression - || newRight == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual - // Visited expression could be null, We need to pass MemberInitExpression - && TryRewriteEntityEquality( - binaryExpression.NodeType, - newLeft, - newRight, - equalsMethod: false, - out var result)) - { - return result; - } - - if (IsConvertedToNullable(newLeft, binaryExpression.Left) - || IsConvertedToNullable(newRight, binaryExpression.Right)) - { - newLeft = ConvertToNullable(newLeft); - newRight = ConvertToNullable(newRight); - } - - if (binaryExpression.NodeType is ExpressionType.Equal or ExpressionType.NotEqual - && TryUseComparer(newLeft, newRight, out var updatedExpression)) - { - if (binaryExpression.NodeType == ExpressionType.NotEqual) - { - updatedExpression = Expression.IsFalse(updatedExpression!); - } - - return updatedExpression!; - } - - return Expression.MakeBinary( - binaryExpression.NodeType, - newLeft, - newRight, - binaryExpression.IsLiftedToNull, - binaryExpression.Method, - binaryExpression.Conversion); - - Expression ProcessGetType(StructuralTypeReferenceExpression typeReference, Type comparisonType, bool match) - { - if (typeReference.StructuralType is not IEntityType entityType - || (entityType.BaseType == null - && !entityType.GetDirectlyDerivedTypes().Any())) - { - // No hierarchy - return Expression.Constant((typeReference.StructuralType.ClrType == comparisonType) == match); - } - - if (entityType.GetAllBaseTypes().Any(e => e.ClrType == comparisonType)) - { - // EntitySet will never contain a type of base type - return Expression.Constant(!match); - } - - var derivedType = entityType.GetDerivedTypesInclusive().SingleOrDefault(et => et.ClrType == comparisonType); - // If no derived type matches then fail the translation - if (derivedType != null) - { - // If the derived type is abstract type then predicate will always be false - if (derivedType.IsAbstract()) - { - return Expression.Constant(!match); - } - - // Or add predicate for matching that particular type discriminator value - // All hierarchies have discriminator property - var discriminatorProperty = entityType.FindDiscriminatorProperty()!; - var boundProperty = BindProperty(typeReference, discriminatorProperty, discriminatorProperty.ClrType); - // KeyValueComparer is not null at runtime - var valueComparer = discriminatorProperty.GetKeyValueComparer(); - - var result = valueComparer.ExtractEqualsBody( - boundProperty!, - Expression.Constant(derivedType.GetDiscriminatorValue(), discriminatorProperty.ClrType)); - - return match ? result : Expression.Not(result); - } - - return QueryCompilationContext.NotTranslatedExpression; - } - - bool IsGetTypeMethodCall(Expression expression, out StructuralTypeReferenceExpression? typeReference) - { - typeReference = null; - if (expression is not MethodCallExpression methodCallExpression - || methodCallExpression.Method != GetTypeMethodInfo) - { - return false; - } - - typeReference = Visit(methodCallExpression.Object) as StructuralTypeReferenceExpression; - return typeReference != null; - } - - static bool IsTypeConstant(Expression expression, out Type? type) - { - type = null; - if (expression is not UnaryExpression - { - NodeType: ExpressionType.Convert or ExpressionType.ConvertChecked, - Operand: ConstantExpression constantExpression - }) - { - return false; - } - - type = constantExpression.Value as Type; - return type != null; - } - } - - private static bool TryUseComparer( - Expression? newLeft, - Expression? newRight, - out Expression? updatedExpression) - { - updatedExpression = null; - - if (newLeft == null - || newRight == null) - { - return false; - } - - var property = FindProperty(newLeft) ?? FindProperty(newRight); - var comparer = property?.GetValueComparer(); - - if (comparer == null) - { - return false; - } - - MethodInfo? objectEquals = null; - MethodInfo? exactMatch = null; - - var converter = property?.GetValueConverter(); - foreach (var candidate in comparer - .GetType() - .GetMethods(BindingFlags.Public | BindingFlags.Instance) - .Where( - m => m.Name == "Equals" && m.GetParameters().Length == 2) - .ToList()) - { - var parameters = candidate.GetParameters(); - var leftType = parameters[0].ParameterType; - var rightType = parameters[1].ParameterType; - - if (leftType == typeof(object) - && rightType == typeof(object)) - { - objectEquals = candidate; - continue; - } - - var matchingLeft = leftType.IsAssignableFrom(newLeft.Type) - ? newLeft - : converter != null - && leftType.IsAssignableFrom(converter.ModelClrType) - && converter.ProviderClrType.IsAssignableFrom(newLeft.Type) - ? ReplacingExpressionVisitor.Replace( - converter.ConvertFromProviderExpression.Parameters.Single(), - newLeft, - converter.ConvertFromProviderExpression.Body) - : null; - - var matchingRight = rightType.IsAssignableFrom(newRight.Type) - ? newRight - : converter != null - && rightType.IsAssignableFrom(converter.ModelClrType) - && converter.ProviderClrType.IsAssignableFrom(newRight.Type) - ? ReplacingExpressionVisitor.Replace( - converter.ConvertFromProviderExpression.Parameters.Single(), - newRight, - converter.ConvertFromProviderExpression.Body) - : null; - - if (matchingLeft != null && matchingRight != null) - { - exactMatch = candidate; - newLeft = matchingLeft; - newRight = matchingRight; - break; - } - } - - if (exactMatch == null - && (!property!.ClrType.IsAssignableFrom(newLeft.Type)) - || !property!.ClrType.IsAssignableFrom(newRight.Type)) - { - return false; - } - - updatedExpression = - exactMatch != null - ? Expression.Call( - Expression.Constant(comparer, comparer.GetType()), - exactMatch, - newLeft, - newRight) - : Expression.Call( - Expression.Constant(comparer, comparer.GetType()), - objectEquals!, - Expression.Convert(newLeft, typeof(object)), - Expression.Convert(newRight, typeof(object))); - - return true; - } - - /// - /// 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. - /// - protected override Expression VisitConditional(ConditionalExpression conditionalExpression) - { - var test = Visit(conditionalExpression.Test); - var ifTrue = Visit(conditionalExpression.IfTrue); - var ifFalse = Visit(conditionalExpression.IfFalse); - - if (test == QueryCompilationContext.NotTranslatedExpression - || ifTrue == QueryCompilationContext.NotTranslatedExpression - || ifFalse == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (test.Type == typeof(bool?)) - { - test = Expression.Equal(test, Expression.Constant(true, typeof(bool?))); - } - - if (IsConvertedToNullable(ifTrue, conditionalExpression.IfTrue) - || IsConvertedToNullable(ifFalse, conditionalExpression.IfFalse)) - { - ifTrue = ConvertToNullable(ifTrue); - ifFalse = ConvertToNullable(ifFalse); - } - - return Expression.Condition(test, ifTrue, ifFalse); - } - - /// - /// 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. - /// - protected override Expression VisitExtension(Expression extensionExpression) - { - switch (extensionExpression) - { - case EntityProjectionExpression: - case StructuralTypeReferenceExpression: - return extensionExpression; - - case StructuralTypeShaperExpression shaper: - return new StructuralTypeReferenceExpression(shaper); - - case ProjectionBindingExpression projectionBindingExpression: - return ((KafkaQueryExpression)projectionBindingExpression.QueryExpression) - .GetProjection(projectionBindingExpression); - - default: - return QueryCompilationContext.NotTranslatedExpression; - } - } - - /// - /// 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. - /// - protected override Expression VisitInvocation(InvocationExpression invocationExpression) - => QueryCompilationContext.NotTranslatedExpression; - - /// - /// 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. - /// - protected override Expression VisitLambda(Expression lambdaExpression) - => throw new InvalidOperationException(CoreStrings.TranslationFailed(lambdaExpression.Print())); - - /// - /// 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. - /// - protected override Expression VisitListInit(ListInitExpression listInitExpression) - => QueryCompilationContext.NotTranslatedExpression; - - /// - /// 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. - /// - protected override Expression VisitMember(MemberExpression memberExpression) - { - var innerExpression = Visit(memberExpression.Expression); - - // when visiting unary we remove converts from nullable to non-nullable - // however if this happens for memberExpression.Expression we are unable to bind - if (innerExpression != null - && memberExpression.Expression != null - && innerExpression.Type != memberExpression.Expression.Type - && innerExpression.Type.IsNullableType() - && innerExpression.Type.UnwrapNullableType() == memberExpression.Expression.Type) - { - innerExpression = Expression.Convert(innerExpression, memberExpression.Expression.Type); - } - - if (memberExpression.Expression != null - && innerExpression == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (TryBindMember(innerExpression, MemberIdentity.Create(memberExpression.Member), memberExpression.Type) is Expression result) - { - return result; - } - - var updatedMemberExpression = (Expression)memberExpression.Update(innerExpression); - if (innerExpression != null - && innerExpression.Type.IsNullableType() - && ShouldApplyNullProtectionForMemberAccess(innerExpression.Type, memberExpression.Member.Name)) - { - updatedMemberExpression = ConvertToNullable(updatedMemberExpression); - - return Expression.Condition( - // Since inner is nullable type this is fine. - Expression.Equal(innerExpression, Expression.Default(innerExpression.Type)), - Expression.Default(updatedMemberExpression.Type), - updatedMemberExpression); - } - - return updatedMemberExpression; - - static bool ShouldApplyNullProtectionForMemberAccess(Type callerType, string memberName) - => !(callerType.IsGenericType - && callerType.GetGenericTypeDefinition() == typeof(Nullable<>) - && memberName is nameof(Nullable.Value) or nameof(Nullable.HasValue)); - } - - /// - /// 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. - /// - protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment) - { - var expression = Visit(memberAssignment.Expression); - if (expression == QueryCompilationContext.NotTranslatedExpression) - { - return memberAssignment.Update(Expression.Convert(expression, memberAssignment.Expression.Type)); - } - - if (IsConvertedToNullable(expression, memberAssignment.Expression)) - { - expression = ConvertToNonNullable(expression); - } - - return memberAssignment.Update(expression); - } - - /// - /// 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. - /// - protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression) - { - var newExpression = Visit(memberInitExpression.NewExpression); - if (newExpression == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var newBindings = new MemberBinding[memberInitExpression.Bindings.Count]; - for (var i = 0; i < newBindings.Length; i++) - { - if (memberInitExpression.Bindings[i].BindingType != MemberBindingType.Assignment) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]); - if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression - && unaryExpression.Operand == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - } - - return memberInitExpression.Update((NewExpression)newExpression, newBindings); - } - - /// - /// 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. - /// - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod) - { - return methodCallExpression; - } - - // EF.Property case - if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)) - { - return TryBindMember(Visit(source), MemberIdentity.Create(propertyName), methodCallExpression.Type) - ?? throw new InvalidOperationException(CoreStrings.QueryUnableToTranslateEFProperty(methodCallExpression.Print())); - } - - // EF Indexer property - if (methodCallExpression.TryGetIndexerArguments(_model, out source, out propertyName)) - { - return TryBindMember(Visit(source), MemberIdentity.Create(propertyName), methodCallExpression.Type) - ?? QueryCompilationContext.NotTranslatedExpression; - } - - // Subquery case - var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression); - if (subqueryTranslation != null) - { - var subquery = (KafkaQueryExpression)subqueryTranslation.QueryExpression; - if (subqueryTranslation.ResultCardinality == ResultCardinality.Enumerable) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var shaperExpression = subqueryTranslation.ShaperExpression; - var innerExpression = shaperExpression; - Type? convertedType = null; - if (shaperExpression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression) - { - convertedType = unaryExpression.Type; - innerExpression = unaryExpression.Operand; - } - - if (innerExpression is StructuralTypeShaperExpression shaper - && (convertedType == null - || convertedType.IsAssignableFrom(shaper.Type))) - { - return new StructuralTypeReferenceExpression(subqueryTranslation.UpdateShaperExpression(innerExpression)); - } - - if (!(innerExpression is ProjectionBindingExpression projectionBindingExpression - && (convertedType == null - || convertedType.MakeNullable() == innerExpression.Type))) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (projectionBindingExpression.ProjectionMember == null) - { - // We don't lift scalar subquery with client eval - return QueryCompilationContext.NotTranslatedExpression; - } - - return ProcessSingleResultScalar( - subquery, - subquery.GetProjection(projectionBindingExpression), - methodCallExpression.Type); - } - - if (methodCallExpression.Method == LikeMethodInfo - || methodCallExpression.Method == LikeMethodInfoWithEscape) - { - // EF.Functions.Like - var visitedArguments = new Expression[3]; - visitedArguments[2] = Expression.Constant(null, typeof(string)); - // Skip first DbFunctions argument - for (var i = 1; i < methodCallExpression.Arguments.Count; i++) - { - var argument = Visit(methodCallExpression.Arguments[i]); - if (TranslationFailed(methodCallExpression.Arguments[i], argument)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - visitedArguments[i - 1] = argument; - } - - return Expression.Call(KafkaLikeMethodInfo, visitedArguments); - } - - if (methodCallExpression.Method == RandomMethodInfo) - { - return Expression.Call(Expression.New(typeof(Random)), RandomNextDoubleMethodInfo); - } - - Expression? @object = null; - Expression[] arguments; - var method = methodCallExpression.Method; - - if (method.Name == nameof(object.Equals) - && methodCallExpression is { Object: not null, Arguments.Count: 1 }) - { - var left = Visit(methodCallExpression.Object); - var right = Visit(methodCallExpression.Arguments[0]); - - if (TryRewriteEntityEquality( - ExpressionType.Equal, - left == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Object : left, - right == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[0] : right, - equalsMethod: true, - out var result)) - { - return result; - } - - if (TranslationFailed(methodCallExpression.Object, left) - || TranslationFailed(methodCallExpression.Arguments[0], right)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - @object = left; - arguments = new[] { right }; - } - else if (method.Name == nameof(object.Equals) - && methodCallExpression.Object == null - && methodCallExpression.Arguments.Count == 2) - { - if (methodCallExpression.Arguments[0].Type == typeof(object[]) - && methodCallExpression.Arguments[0] is NewArrayExpression) - { - return Visit( - ConvertObjectArrayEqualityComparison( - methodCallExpression.Arguments[0], methodCallExpression.Arguments[1])); - } - - var left = Visit(methodCallExpression.Arguments[0]); - var right = Visit(methodCallExpression.Arguments[1]); - - if (TryUseComparer(left, right, out var updatedExpression)) - { - return updatedExpression!; - } - - if (TryRewriteEntityEquality( - ExpressionType.Equal, - left == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[0] : left, - right == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[1] : right, - equalsMethod: true, - out var result)) - { - return result; - } - - if (TranslationFailed(methodCallExpression.Arguments[0], left) - || TranslationFailed(methodCallExpression.Arguments[1], right)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - arguments = new[] { left, right }; - } - else if (method.IsGenericMethod - && method.GetGenericMethodDefinition().Equals(EnumerableMethods.Contains)) - { - var enumerable = Visit(methodCallExpression.Arguments[0]); - var item = Visit(methodCallExpression.Arguments[1]); - - if (TryRewriteContainsEntity( - enumerable, - item == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[1] : item, - out var result)) - { - return result; - } - - if (TranslationFailed(methodCallExpression.Arguments[0], enumerable) - || TranslationFailed(methodCallExpression.Arguments[1], item)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - arguments = new[] { enumerable, item }; - } - else if (methodCallExpression.Arguments.Count == 1 - && method.IsContainsMethod()) - { - var enumerable = Visit(methodCallExpression.Object); - var item = Visit(methodCallExpression.Arguments[0]); - - if (TryRewriteContainsEntity( - enumerable, - item == QueryCompilationContext.NotTranslatedExpression ? methodCallExpression.Arguments[0] : item, - out var result)) - { - return result; - } - - if (TranslationFailed(methodCallExpression.Object, enumerable) - || TranslationFailed(methodCallExpression.Arguments[0], item)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - @object = enumerable; - arguments = new[] { item }; - } - else - { - @object = Visit(methodCallExpression.Object); - if (TranslationFailed(methodCallExpression.Object, @object)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - arguments = new Expression[methodCallExpression.Arguments.Count]; - for (var i = 0; i < arguments.Length; i++) - { - var argument = Visit(methodCallExpression.Arguments[i]); - if (TranslationFailed(methodCallExpression.Arguments[i], argument)) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - arguments[i] = argument; - } - } - - // if the nullability of arguments change, we have no easy/reliable way to adjust the actual methodInfo to match the new type, - // so we are forced to cast back to the original type - var parameterTypes = methodCallExpression.Method.GetParameters().Select(p => p.ParameterType).ToArray(); - for (var i = 0; i < arguments.Length; i++) - { - var argument = arguments[i]; - if (IsConvertedToNullable(argument, methodCallExpression.Arguments[i]) - && !parameterTypes[i].IsAssignableFrom(argument.Type)) - { - argument = ConvertToNonNullable(argument); - } - - arguments[i] = argument; - } - - // if object is nullable, add null safeguard before calling the function - // we special-case Nullable<>.GetValueOrDefault, which doesn't need the safeguard - if (methodCallExpression.Object != null - && @object!.Type.IsNullableType() - && methodCallExpression.Method.Name != nameof(Nullable.GetValueOrDefault)) - { - var result = (Expression)methodCallExpression.Update( - Expression.Convert(@object, methodCallExpression.Object.Type), - arguments); - - result = ConvertToNullable(result); - var objectNullCheck = Expression.Equal(@object, Expression.Constant(null, @object.Type)); - // instance.Equals(argument) should translate to - // instance == null ? argument == null : instance.Equals(argument) - if (method.Name == nameof(object.Equals)) - { - var argument = arguments[0]; - if (argument.NodeType == ExpressionType.Convert - && argument is UnaryExpression unaryExpression - && argument.Type == unaryExpression.Operand.Type.UnwrapNullableType()) - { - argument = unaryExpression.Operand; - } - - if (!argument.Type.IsNullableType()) - { - argument = Expression.Convert(argument, argument.Type.MakeNullable()); - } - - return Expression.Condition( - objectNullCheck, - ConvertToNullable(Expression.Equal(argument, Expression.Constant(null, argument.Type))), - result); - } - - return Expression.Condition(objectNullCheck, Expression.Constant(null, result.Type), result); - } - - return methodCallExpression.Update(@object, arguments); - } - - /// - /// 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. - /// - protected override Expression VisitNew(NewExpression newExpression) - { - var newArguments = new List(); - foreach (var argument in newExpression.Arguments) - { - var newArgument = Visit(argument); - if (newArgument == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (IsConvertedToNullable(newArgument, argument)) - { - newArgument = ConvertToNonNullable(newArgument); - } - - newArguments.Add(newArgument); - } - - return newExpression.Update(newArguments); - } - - /// - /// 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. - /// - protected override Expression VisitNewArray(NewArrayExpression newArrayExpression) - { - var newExpressions = new List(); - foreach (var expression in newArrayExpression.Expressions) - { - var newExpression = Visit(expression); - if (newExpression == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (IsConvertedToNullable(newExpression, expression)) - { - newExpression = ConvertToNonNullable(newExpression); - } - - newExpressions.Add(newExpression); - } - - return newArrayExpression.Update(newExpressions); - } - - /// - /// 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. - /// - protected override Expression VisitParameter(ParameterExpression parameterExpression) - { - if (parameterExpression.Name?.StartsWith(QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal) == true) - { - return Expression.Call( - GetParameterValueMethodInfo.MakeGenericMethod(parameterExpression.Type), - QueryCompilationContext.QueryContextParameter, - Expression.Constant(parameterExpression.Name)); - } - - throw new InvalidOperationException(CoreStrings.TranslationFailed(parameterExpression.Print())); - } - - /// - /// 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. - /// - protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) - { - if (typeBinaryExpression.NodeType == ExpressionType.TypeIs - && Visit(typeBinaryExpression.Expression) is StructuralTypeReferenceExpression typeReference) - { - if (typeReference.StructuralType is not IEntityType entityType) - { - return Expression.Constant(typeReference.StructuralType.ClrType == typeBinaryExpression.TypeOperand); - } - - if (entityType.GetAllBaseTypesInclusive().Any(et => et.ClrType == typeBinaryExpression.TypeOperand)) - { - return Expression.Constant(true); - } - - var derivedType = entityType.GetDerivedTypes().SingleOrDefault(et => et.ClrType == typeBinaryExpression.TypeOperand); - if (derivedType != null) - { - // All hierarchies have discriminator property - var discriminatorProperty = entityType.FindDiscriminatorProperty()!; - var boundProperty = BindProperty(typeReference, discriminatorProperty, discriminatorProperty.ClrType); - // KeyValueComparer is not null at runtime - var valueComparer = discriminatorProperty.GetKeyValueComparer(); - - var equals = valueComparer.ExtractEqualsBody( - boundProperty!, - Expression.Constant(derivedType.GetDiscriminatorValue(), discriminatorProperty.ClrType)); - - foreach (var derivedDerivedType in derivedType.GetDerivedTypes()) - { - equals = Expression.OrElse( - equals, - valueComparer.ExtractEqualsBody( - boundProperty!, - Expression.Constant(derivedDerivedType.GetDiscriminatorValue(), discriminatorProperty.ClrType))); - } - - return equals; - } - } - - return QueryCompilationContext.NotTranslatedExpression; - } - - /// - /// 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. - /// - protected override Expression VisitUnary(UnaryExpression unaryExpression) - { - var newOperand = Visit(unaryExpression.Operand); - if (newOperand == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (newOperand is StructuralTypeReferenceExpression typeReference - && unaryExpression.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs) - { - return typeReference.Convert(unaryExpression.Type); - } - - if (unaryExpression.NodeType == ExpressionType.Convert - && newOperand.Type == unaryExpression.Type) - { - return newOperand; - } - - if (unaryExpression.NodeType == ExpressionType.Convert - && IsConvertedToNullable(newOperand, unaryExpression)) - { - return newOperand; - } - - var result = (Expression)Expression.MakeUnary(unaryExpression.NodeType, newOperand, unaryExpression.Type); - if (result is UnaryExpression - { - NodeType: ExpressionType.Convert, - Operand: UnaryExpression { NodeType: ExpressionType.Convert } innerUnary - } outerUnary) - { - var innerMostType = innerUnary.Operand.Type; - var intermediateType = innerUnary.Type; - var outerMostType = outerUnary.Type; - - if (outerMostType == innerMostType - && intermediateType == innerMostType.UnwrapNullableType()) - { - result = innerUnary.Operand; - } - else if (outerMostType == typeof(object) - && intermediateType == innerMostType.UnwrapNullableType()) - { - result = Expression.Convert(innerUnary.Operand, typeof(object)); - } - } - - return result; - } - - private Expression? TryBindMember(Expression? source, MemberIdentity member, Type type) - { - if (source is not StructuralTypeReferenceExpression typeReference) - { - return null; - } - - var entityType = typeReference.StructuralType; - - var property = member.MemberInfo != null - ? entityType.FindProperty(member.MemberInfo) - : entityType.FindProperty(member.Name!); - - if (property != null) - { - return BindProperty(typeReference, property, type); - } - - AddTranslationErrorDetails( - CoreStrings.QueryUnableToTranslateMember( - member.Name, - typeReference.StructuralType.DisplayName())); - - return null; - } - - private Expression? BindProperty(StructuralTypeReferenceExpression typeReference, IProperty property, Type type) - { - if (typeReference.Parameter != null) - { - var valueBufferExpression = Visit(typeReference.Parameter.ValueBufferExpression); - if (valueBufferExpression == QueryCompilationContext.NotTranslatedExpression) - { - return null; - } - - var result = ((EntityProjectionExpression)valueBufferExpression).BindProperty(property); - - // if the result type change was just nullability change e.g from int to int? - // we want to preserve the new type for null propagation - return result.Type != type - && !(result.Type.IsNullableType() - && !type.IsNullableType() - && result.Type.UnwrapNullableType() == type) - ? Expression.Convert(result, type) - : result; - } - - if (typeReference.Subquery != null) - { - var entityShaper = (StructuralTypeShaperExpression)typeReference.Subquery.ShaperExpression; - var kafkaQueryExpression = (KafkaQueryExpression)typeReference.Subquery.QueryExpression; - - var projectionBindingExpression = (ProjectionBindingExpression)entityShaper.ValueBufferExpression; - var entityProjectionExpression = (EntityProjectionExpression)kafkaQueryExpression.GetProjection( - projectionBindingExpression); - var readValueExpression = entityProjectionExpression.BindProperty(property); - - return ProcessSingleResultScalar( - kafkaQueryExpression, - readValueExpression, - type); - } - - return null; - } - - private static Expression ProcessSingleResultScalar( - KafkaQueryExpression kafkaQueryExpression, - Expression readValueExpression, - Type type) - { - if (kafkaQueryExpression.ServerQueryExpression is not NewExpression) - { - // The terminating operator is not applied - // It is of FirstOrDefault kind - // So we change to single column projection and then apply it. - kafkaQueryExpression.ReplaceProjection( - new Dictionary { { new ProjectionMember(), readValueExpression } }); - kafkaQueryExpression.ApplyProjection(); - } - - var serverQuery = kafkaQueryExpression.ServerQueryExpression; - serverQuery = ((LambdaExpression)((NewExpression)serverQuery).Arguments[0]).Body; - if (serverQuery is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression - && unaryExpression.Type == typeof(object)) - { - serverQuery = unaryExpression.Operand; - } - - var valueBufferVariable = Expression.Variable(typeof(ValueBuffer)); - var readExpression = valueBufferVariable.CreateValueBufferReadValueExpression(type, index: 0, property: null); - return Expression.Block( - variables: new[] { valueBufferVariable }, - Expression.Assign(valueBufferVariable, serverQuery), - Expression.Condition( - Expression.MakeMemberAccess(valueBufferVariable, ValueBufferIsEmpty), - Expression.Default(type), - readExpression)); - } - - [UsedImplicitly] - private static T GetParameterValue(QueryContext queryContext, string parameterName) - => (T)queryContext.ParameterValues[parameterName]!; - - private static bool IsConvertedToNullable(Expression result, Expression original) - => result.Type.IsNullableType() - && !original.Type.IsNullableType() - && result.Type.UnwrapNullableType() == original.Type; - - private static Expression ConvertToNullable(Expression expression) - => !expression.Type.IsNullableType() - ? Expression.Convert(expression, expression.Type.MakeNullable()) - : expression; - - private static Expression ConvertToNonNullable(Expression expression) - => expression.Type.IsNullableType() - ? Expression.Convert(expression, expression.Type.UnwrapNullableType()) - : expression; - - private static IProperty? FindProperty(Expression? expression) - { - if (expression?.NodeType == ExpressionType.Convert - && expression.Type == typeof(object)) - { - expression = ((UnaryExpression)expression).Operand; - } - - if (expression?.NodeType == ExpressionType.Convert - && expression.Type.IsNullableType() - && expression is UnaryExpression unaryExpression - && (expression.Type.UnwrapNullableType() == unaryExpression.Type - || expression.Type == unaryExpression.Type)) - { - expression = unaryExpression.Operand; - } - - if (expression is MethodCallExpression { Method.IsGenericMethod: true } readValueMethodCall - && readValueMethodCall.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod) - { - return readValueMethodCall.Arguments[2].GetConstantValue(); - } - - return null; - } - - private bool TryRewriteContainsEntity(Expression? source, Expression item, [NotNullWhen(true)] out Expression? result) - { - result = null; - - if (item is not StructuralTypeReferenceExpression { StructuralType: IEntityType entityType }) - { - return false; - } - - var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties; - if (primaryKeyProperties == null) - { - throw new InvalidOperationException( - CoreStrings.EntityEqualityOnKeylessEntityNotSupported( - nameof(Queryable.Contains), entityType.DisplayName())); - } - - if (primaryKeyProperties.Count > 1) - { - throw new InvalidOperationException( - CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported( - nameof(Queryable.Contains), entityType.DisplayName())); - } - - var property = primaryKeyProperties[0]; - Expression rewrittenSource; - switch (source) - { - case ConstantExpression constantExpression: - var values = constantExpression.GetConstantValue(); - var propertyValueList = - (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(property.ClrType.MakeNullable()))!; - var propertyGetter = property.GetGetter(); - foreach (var value in values) - { - propertyValueList.Add(propertyGetter.GetClrValue(value)); - } - - rewrittenSource = Expression.Constant(propertyValueList); - break; - - case MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression - when methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: - var parameterName = methodCallExpression.Arguments[1].GetConstantValue(); - var lambda = Expression.Lambda( - Expression.Call( - ParameterListValueExtractorMethod.MakeGenericMethod(entityType.ClrType, property.ClrType.MakeNullable()), - QueryCompilationContext.QueryContextParameter, - Expression.Constant(parameterName, typeof(string)), - Expression.Constant(property, typeof(IProperty))), - QueryCompilationContext.QueryContextParameter - ); - - var newParameterName = - $"{RuntimeParameterPrefix}" - + $"{parameterName[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}"; - - rewrittenSource = _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda); - break; - - default: - return false; - } - - result = Visit( - Expression.Call( - EnumerableMethods.Contains.MakeGenericMethod(property.ClrType.MakeNullable()), - rewrittenSource, - CreatePropertyAccessExpression(item, property))); - - return true; - } - - private bool TryRewriteEntityEquality( - ExpressionType nodeType, - Expression left, - Expression right, - bool equalsMethod, - [NotNullWhen(true)] out Expression? result) - { - var leftEntityReference = left is StructuralTypeReferenceExpression { StructuralType: IEntityType } l ? l : null; - var rightEntityReference = right is StructuralTypeReferenceExpression { StructuralType: IEntityType } r ? r : null; - - if (leftEntityReference == null - && rightEntityReference == null) - { - result = null; - return false; - } - - if (IsNullConstantExpression(left) - || IsNullConstantExpression(right)) - { - var nonNullEntityReference = (IsNullConstantExpression(left) ? rightEntityReference : leftEntityReference)!; - var entityType1 = (IEntityType)nonNullEntityReference.StructuralType; - var primaryKeyProperties1 = entityType1.FindPrimaryKey()?.Properties; - if (primaryKeyProperties1 == null) - { - throw new InvalidOperationException( - CoreStrings.EntityEqualityOnKeylessEntityNotSupported( - nodeType == ExpressionType.Equal - ? equalsMethod ? nameof(object.Equals) : "==" - : equalsMethod - ? "!" + nameof(object.Equals) - : "!=", - entityType1.DisplayName())); - } - - result = Visit( - primaryKeyProperties1.Select( - p => - Expression.MakeBinary( - nodeType, CreatePropertyAccessExpression(nonNullEntityReference, p), - Expression.Constant(null, p.ClrType.MakeNullable()))) - .Aggregate((l, r) => nodeType == ExpressionType.Equal ? Expression.OrElse(l, r) : Expression.AndAlso(l, r))); - - return true; - } - - var leftEntityType = (IEntityType?)leftEntityReference?.StructuralType; - var rightEntityType = (IEntityType?)rightEntityReference?.StructuralType; - var entityType = leftEntityType ?? rightEntityType; - - Check.DebugAssert(entityType != null, "At least either side should be entityReference so entityType should be non-null."); - - if (leftEntityType != null - && rightEntityType != null - && leftEntityType.GetRootType() != rightEntityType.GetRootType()) - { - result = Expression.Constant(false); - return true; - } - - var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties; - if (primaryKeyProperties == null) - { - throw new InvalidOperationException( - CoreStrings.EntityEqualityOnKeylessEntityNotSupported( - nodeType == ExpressionType.Equal - ? equalsMethod ? nameof(object.Equals) : "==" - : equalsMethod - ? "!" + nameof(object.Equals) - : "!=", - entityType.DisplayName())); - } - - if (primaryKeyProperties.Count > 1 - && (leftEntityReference?.Subquery != null - || rightEntityReference?.Subquery != null)) - { - throw new InvalidOperationException( - CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported( - nodeType == ExpressionType.Equal - ? equalsMethod ? nameof(object.Equals) : "==" - : equalsMethod - ? "!" + nameof(object.Equals) - : "!=", - entityType.DisplayName())); - } - - result = Visit( - primaryKeyProperties.Select( - p => - Expression.MakeBinary( - nodeType, - CreatePropertyAccessExpression(left, p), - CreatePropertyAccessExpression(right, p))) - .Aggregate( - (l, r) => nodeType == ExpressionType.Equal - ? Expression.AndAlso(l, r) - : Expression.OrElse(l, r))); - - return true; - } - - private Expression CreatePropertyAccessExpression(Expression target, IProperty property) - { - switch (target) - { - case ConstantExpression constantExpression: - return Expression.Constant( - constantExpression.Value is null - ? null - : property.GetGetter().GetClrValue(constantExpression.Value), - property.ClrType.MakeNullable()); - - case MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression - when methodCallExpression.Method.GetGenericMethodDefinition() == GetParameterValueMethodInfo: - var parameterName = methodCallExpression.Arguments[1].GetConstantValue(); - var lambda = Expression.Lambda( - Expression.Call( - ParameterValueExtractorMethod.MakeGenericMethod(property.ClrType.MakeNullable()), - QueryCompilationContext.QueryContextParameter, - Expression.Constant(parameterName, typeof(string)), - Expression.Constant(property, typeof(IProperty))), - QueryCompilationContext.QueryContextParameter); - - var newParameterName = - $"{RuntimeParameterPrefix}" - + $"{parameterName[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}"; - - return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda); - - case MemberInitExpression memberInitExpression - when memberInitExpression.Bindings.SingleOrDefault( - mb => mb.Member.Name == property.Name) is MemberAssignment memberAssignment: - return memberAssignment.Expression.Type.IsNullableType() - ? memberAssignment.Expression - : Expression.Convert(memberAssignment.Expression, property.ClrType.MakeNullable()); - - case NewExpression newExpression - when CanEvaluate(newExpression): - return CreatePropertyAccessExpression(GetValue(newExpression), property); - - case MemberInitExpression memberInitExpression - when CanEvaluate(memberInitExpression): - return CreatePropertyAccessExpression(GetValue(memberInitExpression), property); - - default: - return target.CreateEFPropertyExpression(property); - } - } - - private static T? ParameterValueExtractor(QueryContext context, string baseParameterName, IProperty property) - { - var baseParameter = context.ParameterValues[baseParameterName]; - return baseParameter == null ? (T?)(object?)null : (T?)property.GetGetter().GetClrValue(baseParameter); - } - - private static List? ParameterListValueExtractor( - QueryContext context, - string baseParameterName, - IProperty property) - { - if (!(context.ParameterValues[baseParameterName] is IEnumerable baseListParameter)) - { - return null; - } - - var getter = property.GetGetter(); - return baseListParameter.Select(e => e != null ? (TProperty?)getter.GetClrValue(e) : (TProperty?)(object?)null).ToList(); - } - - private static ConstantExpression GetValue(Expression expression) - => Expression.Constant( - Expression.Lambda>(Expression.Convert(expression, typeof(object))) - .Compile(preferInterpretation: true) - .Invoke(), - expression.Type); - - private static bool CanEvaluate(Expression expression) - { -#pragma warning disable IDE0066 // Convert switch statement to expression - switch (expression) -#pragma warning restore IDE0066 // Convert switch statement to expression - { - case ConstantExpression: - return true; - - case NewExpression newExpression: - return newExpression.Arguments.All(CanEvaluate); - - case MemberInitExpression memberInitExpression: - return CanEvaluate(memberInitExpression.NewExpression) - && memberInitExpression.Bindings.All( - mb => mb is MemberAssignment memberAssignment && CanEvaluate(memberAssignment.Expression)); - - default: - return false; - } - } - - private static Expression ConvertObjectArrayEqualityComparison(Expression left, Expression right) - { - var leftExpressions = ((NewArrayExpression)left).Expressions; - var rightExpressions = ((NewArrayExpression)right).Expressions; - - return leftExpressions.Zip( - rightExpressions, - (l, r) => - { - l = RemoveObjectConvert(l); - r = RemoveObjectConvert(r); - if (l.Type.IsNullableType()) - { - r = r.Type.IsNullableType() ? r : Expression.Convert(r, l.Type); - } - else if (r.Type.IsNullableType()) - { - l = l.Type.IsNullableType() ? l : Expression.Convert(l, r.Type); - } - - return ExpressionExtensions.CreateEqualsExpression(l, r); - }) - .Aggregate((a, b) => Expression.AndAlso(a, b)); - - static Expression RemoveObjectConvert(Expression expression) - => expression is UnaryExpression unaryExpression - && expression.Type == typeof(object) - && expression.NodeType == ExpressionType.Convert - ? unaryExpression.Operand - : expression; - } - - private static bool IsNullConstantExpression(Expression expression) - => expression is ConstantExpression { Value: null }; - - [DebuggerStepThrough] - private static bool TranslationFailed(Expression? original, Expression? translation) - => original != null - && (translation == QueryCompilationContext.NotTranslatedExpression || translation is StructuralTypeReferenceExpression); - - private static bool KafkaLike(string matchExpression, string pattern, string escapeCharacter) - { - //TODO: this fixes https://github.com/aspnet/EntityFramework/issues/8656 by insisting that - // the "escape character" is a string but just using the first character of that string, - // but we may later want to allow the complete string as the "escape character" - // in which case we need to change the way we construct the regex below. - var singleEscapeCharacter = - (escapeCharacter == null || escapeCharacter.Length == 0) - ? (char?)null - : escapeCharacter.First(); - - if (matchExpression == null - || pattern == null) - { - return false; - } - - if (matchExpression.Equals(pattern, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (matchExpression.Length == 0 - || pattern.Length == 0) - { - return false; - } - - var escapeRegexCharsPattern - = singleEscapeCharacter == null - ? DefaultEscapeRegexCharsPattern - : BuildEscapeRegexCharsPattern(RegexSpecialChars.Where(c => c != singleEscapeCharacter)); - - var regexPattern - = Regex.Replace( - pattern, - escapeRegexCharsPattern, - c => @"\" + c, - default, - RegexTimeout); - - var stringBuilder = new StringBuilder(); - - for (var i = 0; i < regexPattern.Length; i++) - { - var c = regexPattern[i]; - var escaped = i > 0 && regexPattern[i - 1] == singleEscapeCharacter; - - switch (c) - { - case '_': - { - stringBuilder.Append(escaped ? '_' : '.'); - break; - } - case '%': - { - stringBuilder.Append(escaped ? "%" : ".*"); - break; - } - default: - { - if (c != singleEscapeCharacter) - { - stringBuilder.Append(c); - } - - break; - } - } - } - - regexPattern = stringBuilder.ToString(); - - return Regex.IsMatch( - matchExpression, - @"\A" + regexPattern + @"\s*\z", - RegexOptions.IgnoreCase | RegexOptions.Singleline, - RegexTimeout); - } - - private sealed class EntityReferenceFindingExpressionVisitor : ExpressionVisitor - { - private bool _found; - - public bool Find(Expression expression) - { - _found = false; - - Visit(expression); - - return _found; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (_found) - { - return expression; - } - - if (expression is StructuralTypeReferenceExpression) - { - _found = true; - return expression; - } - - return base.Visit(expression); - } - } - - private sealed class StructuralTypeReferenceExpression : Expression - { - public StructuralTypeReferenceExpression(StructuralTypeShaperExpression parameter) - { - Parameter = parameter; - StructuralType = parameter.StructuralType; - } - - public StructuralTypeReferenceExpression(ShapedQueryExpression subquery) - { - Subquery = subquery; - StructuralType = ((StructuralTypeShaperExpression)subquery.ShaperExpression).StructuralType; - } - - private StructuralTypeReferenceExpression(StructuralTypeReferenceExpression typeReference, IEntityType type) - { - Parameter = typeReference.Parameter; - Subquery = typeReference.Subquery; - StructuralType = type; - } - - public new StructuralTypeShaperExpression? Parameter { get; } - public ShapedQueryExpression? Subquery { get; } - public ITypeBase StructuralType { get; } - - public override Type Type - => StructuralType.ClrType; - - public override ExpressionType NodeType - => ExpressionType.Extension; - - public Expression Convert(Type type) - { - if (type == typeof(object) // Ignore object conversion - || type.IsAssignableFrom(Type)) // Ignore casting to base type/interface - { - return this; - } - - return StructuralType is IEntityType entityType - && entityType.GetDerivedTypes().FirstOrDefault(et => et.ClrType == type) is IEntityType derivedEntityType - ? new StructuralTypeReferenceExpression(this, derivedEntityType) - : QueryCompilationContext.NotTranslatedExpression; - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaProjectionBindingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal8/KafkaProjectionBindingExpressionVisitor.cs deleted file mode 100644 index a1ec6cbc..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaProjectionBindingExpressionVisitor.cs +++ /dev/null @@ -1,532 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaProjectionBindingExpressionVisitor : ExpressionVisitor -{ - private readonly KafkaQueryableMethodTranslatingExpressionVisitor _queryableMethodTranslatingExpressionVisitor; - private readonly KafkaExpressionTranslatingExpressionVisitor _expressionTranslatingExpressionVisitor; - - private KafkaQueryExpression _queryExpression; - private bool _indexBasedBinding; - - private Dictionary? _entityProjectionCache; - - private readonly Dictionary _projectionMapping = new(); - private List? _clientProjections; - private readonly Stack _projectionMembers = new(); - - /// - /// 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. - /// - public KafkaProjectionBindingExpressionVisitor( - KafkaQueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor, - KafkaExpressionTranslatingExpressionVisitor expressionTranslatingExpressionVisitor) - { - _queryableMethodTranslatingExpressionVisitor = queryableMethodTranslatingExpressionVisitor; - _expressionTranslatingExpressionVisitor = expressionTranslatingExpressionVisitor; - _queryExpression = null!; - } - - /// - /// 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. - /// - public virtual Expression Translate(KafkaQueryExpression queryExpression, Expression expression) - { - _queryExpression = queryExpression; - _indexBasedBinding = false; - - _projectionMembers.Push(new ProjectionMember()); - var result = Visit(expression); - - if (result == QueryCompilationContext.NotTranslatedExpression) - { - _indexBasedBinding = true; - _projectionMapping.Clear(); - _entityProjectionCache = new Dictionary(); - _clientProjections = new List(); - - result = Visit(expression); - - _queryExpression.ReplaceProjection(_clientProjections); - _clientProjections = null; - } - else - { - _queryExpression.ReplaceProjection(_projectionMapping); - _projectionMapping.Clear(); - } - - _queryExpression = null!; - _projectionMembers.Clear(); - result = MatchTypes(result, expression.Type); - - return result; - } - - /// - /// 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. - /// - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (expression == null) - { - return null; - } - - if (expression is not (NewExpression or MemberInitExpression or StructuralTypeShaperExpression or IncludeExpression)) - { - if (_indexBasedBinding) - { - switch (expression) - { - case ConstantExpression: - return expression; - - case ProjectionBindingExpression projectionBindingExpression: - var mappedProjection = _queryExpression.GetProjection(projectionBindingExpression); - if (mappedProjection is EntityProjectionExpression entityProjection) - { - return AddClientProjection(entityProjection, typeof(ValueBuffer)); - } - - if (mappedProjection is not KafkaQueryExpression) - { - return AddClientProjection(mappedProjection, expression.Type.MakeNullable()); - } - - throw new InvalidOperationException(CoreStrings.TranslationFailed(projectionBindingExpression.Print())); - - case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression: - { - var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery( - materializeCollectionNavigationExpression.Subquery)!; - _clientProjections!.Add(subquery.QueryExpression); - return new CollectionResultShaperExpression( - new ProjectionBindingExpression( - _queryExpression, _clientProjections.Count - 1, typeof(IEnumerable)), - subquery.ShaperExpression, - materializeCollectionNavigationExpression.Navigation, - materializeCollectionNavigationExpression.Navigation.ClrType.GetSequenceType()); - } - - case MethodCallExpression methodCallExpression: - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.DeclaringType == typeof(Enumerable) - && methodCallExpression.Method.Name == nameof(Enumerable.ToList) - && methodCallExpression.Arguments.Count == 1 - && methodCallExpression.Arguments[0].Type.TryGetElementType(typeof(IQueryable<>)) != null) - { - var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery( - methodCallExpression.Arguments[0]); - if (subquery != null) - { - _clientProjections!.Add(subquery.QueryExpression); - return new CollectionResultShaperExpression( - new ProjectionBindingExpression( - _queryExpression, _clientProjections.Count - 1, typeof(IEnumerable)), - subquery.ShaperExpression, - null, - methodCallExpression.Method.GetGenericArguments()[0]); - } - } - else - { - var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression); - if (subquery != null) - { - // This simplifies the check when subquery is translated and can be lifted as scalar. - var scalarTranslation = _expressionTranslatingExpressionVisitor.Translate(subquery); - if (scalarTranslation != null) - { - return AddClientProjection(scalarTranslation, expression.Type.MakeNullable()); - } - - if (subquery.ResultCardinality == ResultCardinality.Enumerable) - { - _clientProjections!.Add(subquery.QueryExpression); - var projectionBindingExpression = new ProjectionBindingExpression( - _queryExpression, _clientProjections.Count - 1, typeof(IEnumerable)); - return new CollectionResultShaperExpression( - projectionBindingExpression, subquery.ShaperExpression, navigation: null, - subquery.ShaperExpression.Type); - } - else - { - _clientProjections!.Add(subquery.QueryExpression); - var projectionBindingExpression = new ProjectionBindingExpression( - _queryExpression, _clientProjections.Count - 1, typeof(ValueBuffer)); - return new SingleResultShaperExpression(projectionBindingExpression, subquery.ShaperExpression); - } - } - } - - break; - } - - var translation = _expressionTranslatingExpressionVisitor.Translate(expression); - return translation != null - ? AddClientProjection(translation, expression.Type.MakeNullable()) - : base.Visit(expression); - } - else - { - var translation = _expressionTranslatingExpressionVisitor.Translate(expression); - if (translation == null) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - _projectionMapping[_projectionMembers.Peek()] = translation; - - return new ProjectionBindingExpression(_queryExpression, _projectionMembers.Peek(), expression.Type.MakeNullable()); - } - } - - return base.Visit(expression); - } - - /// - /// 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. - /// - protected override Expression VisitBinary(BinaryExpression binaryExpression) - { - var left = MatchTypes(Visit(binaryExpression.Left), binaryExpression.Left.Type); - var right = MatchTypes(Visit(binaryExpression.Right), binaryExpression.Right.Type); - - return binaryExpression.Update(left, VisitAndConvert(binaryExpression.Conversion, "VisitBinary"), right); - } - - /// - /// 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. - /// - protected override Expression VisitConditional(ConditionalExpression conditionalExpression) - { - var test = Visit(conditionalExpression.Test); - var ifTrue = Visit(conditionalExpression.IfTrue); - var ifFalse = Visit(conditionalExpression.IfFalse); - - if (test.Type == typeof(bool?)) - { - test = Expression.Equal(test, Expression.Constant(true, typeof(bool?))); - } - - ifTrue = MatchTypes(ifTrue, conditionalExpression.IfTrue.Type); - ifFalse = MatchTypes(ifFalse, conditionalExpression.IfFalse.Type); - - return conditionalExpression.Update(test, ifTrue, ifFalse); - } - - /// - /// 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. - /// - protected override Expression VisitExtension(Expression extensionExpression) - { - if (extensionExpression is StructuralTypeShaperExpression shaper) - { - EntityProjectionExpression entityProjectionExpression; - if (shaper.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression) - { - entityProjectionExpression = - (EntityProjectionExpression)((KafkaQueryExpression)projectionBindingExpression.QueryExpression) - .GetProjection(projectionBindingExpression); - } - else - { - entityProjectionExpression = (EntityProjectionExpression)shaper.ValueBufferExpression; - } - - if (_indexBasedBinding) - { - if (!_entityProjectionCache!.TryGetValue(entityProjectionExpression, out var entityProjectionBinding)) - { - entityProjectionBinding = AddClientProjection(entityProjectionExpression, typeof(ValueBuffer)); - _entityProjectionCache[entityProjectionExpression] = entityProjectionBinding; - } - - return shaper.Update(entityProjectionBinding); - } - - _projectionMapping[_projectionMembers.Peek()] = entityProjectionExpression; - - return shaper.Update( - new ProjectionBindingExpression(_queryExpression, _projectionMembers.Peek(), typeof(ValueBuffer))); - } - - if (extensionExpression is IncludeExpression includeExpression) - { - return _indexBasedBinding - ? base.VisitExtension(includeExpression) - : QueryCompilationContext.NotTranslatedExpression; - } - - throw new InvalidOperationException(CoreStrings.TranslationFailed(extensionExpression.Print())); - } - - /// - /// 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. - /// - protected override ElementInit VisitElementInit(ElementInit elementInit) - => elementInit.Update(elementInit.Arguments.Select(e => MatchTypes(Visit(e), e.Type))); - - /// - /// 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. - /// - protected override Expression VisitMember(MemberExpression memberExpression) - { - var expression = Visit(memberExpression.Expression); - Expression updatedMemberExpression = memberExpression.Update( - expression != null ? MatchTypes(expression, memberExpression.Expression!.Type) : expression); - - if (expression?.Type.IsNullableValueType() == true) - { - var nullableReturnType = memberExpression.Type.MakeNullable(); - if (!memberExpression.Type.IsNullableType()) - { - updatedMemberExpression = Expression.Convert(updatedMemberExpression, nullableReturnType); - } - - updatedMemberExpression = Expression.Condition( - Expression.Equal(expression, Expression.Default(expression.Type)), - Expression.Constant(null, nullableReturnType), - updatedMemberExpression); - } - - return updatedMemberExpression; - } - - /// - /// 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. - /// - protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment) - { - var expression = memberAssignment.Expression; - Expression? visitedExpression; - if (_indexBasedBinding) - { - visitedExpression = Visit(memberAssignment.Expression); - } - else - { - var projectionMember = _projectionMembers.Peek().Append(memberAssignment.Member); - _projectionMembers.Push(projectionMember); - - visitedExpression = Visit(memberAssignment.Expression); - if (visitedExpression == QueryCompilationContext.NotTranslatedExpression) - { - return memberAssignment.Update(Expression.Convert(visitedExpression, memberAssignment.Expression.Type)); - } - - _projectionMembers.Pop(); - } - - visitedExpression = MatchTypes(visitedExpression, expression.Type); - - return memberAssignment.Update(visitedExpression); - } - - /// - /// 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. - /// - protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression) - { - var newExpression = Visit(memberInitExpression.NewExpression); - if (newExpression == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var newBindings = new MemberBinding[memberInitExpression.Bindings.Count]; - for (var i = 0; i < newBindings.Length; i++) - { - if (memberInitExpression.Bindings[i].BindingType != MemberBindingType.Assignment) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]); - if (((MemberAssignment)newBindings[i]).Expression is UnaryExpression { NodeType: ExpressionType.Convert } unaryExpression - && unaryExpression.Operand == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - } - - return memberInitExpression.Update((NewExpression)newExpression, newBindings); - } - - /// - /// 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. - /// - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - var @object = Visit(methodCallExpression.Object); - var arguments = new Expression[methodCallExpression.Arguments.Count]; - for (var i = 0; i < methodCallExpression.Arguments.Count; i++) - { - var argument = methodCallExpression.Arguments[i]; - arguments[i] = MatchTypes(Visit(argument), argument.Type); - } - - Expression updatedMethodCallExpression = methodCallExpression.Update( - @object != null ? MatchTypes(@object, methodCallExpression.Object!.Type) : @object!, - arguments); - - if (@object?.Type.IsNullableType() == true - && !methodCallExpression.Object!.Type.IsNullableType()) - { - var nullableReturnType = methodCallExpression.Type.MakeNullable(); - if (!methodCallExpression.Type.IsNullableType()) - { - updatedMethodCallExpression = Expression.Convert(updatedMethodCallExpression, nullableReturnType); - } - - return Expression.Condition( - Expression.Equal(@object, Expression.Default(@object.Type)), - Expression.Constant(null, nullableReturnType), - updatedMethodCallExpression); - } - - return updatedMethodCallExpression; - } - - /// - /// 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. - /// - protected override Expression VisitNew(NewExpression newExpression) - { - if (newExpression.Arguments.Count == 0) - { - return newExpression; - } - - if (!_indexBasedBinding - && newExpression.Members == null) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var newArguments = new Expression[newExpression.Arguments.Count]; - for (var i = 0; i < newArguments.Length; i++) - { - var argument = newExpression.Arguments[i]; - Expression? visitedArgument; - if (_indexBasedBinding) - { - visitedArgument = Visit(argument); - } - else - { - var projectionMember = _projectionMembers.Peek().Append(newExpression.Members![i]); - _projectionMembers.Push(projectionMember); - visitedArgument = Visit(argument); - if (visitedArgument == QueryCompilationContext.NotTranslatedExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - _projectionMembers.Pop(); - } - - newArguments[i] = MatchTypes(visitedArgument, argument.Type); - } - - return newExpression.Update(newArguments); - } - - /// - /// 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. - /// - protected override Expression VisitNewArray(NewArrayExpression newArrayExpression) - => newArrayExpression.Update(newArrayExpression.Expressions.Select(e => MatchTypes(Visit(e), e.Type))); - - /// - /// 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. - /// - protected override Expression VisitUnary(UnaryExpression unaryExpression) - { - var operand = Visit(unaryExpression.Operand); - - return unaryExpression.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked - && unaryExpression.Type == operand.Type - ? operand - : unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type)); - } - - private static Expression MatchTypes(Expression expression, Type targetType) - { - if (targetType != expression.Type - && targetType.TryGetElementType(typeof(IQueryable<>)) == null) - { - Check.DebugAssert(targetType.MakeNullable() == expression.Type, "Not a nullable to non-nullable conversion"); - - expression = Expression.Convert(expression, targetType); - } - - return expression; - } - - private ProjectionBindingExpression AddClientProjection(Expression expression, Type type) - { - var existingIndex = _clientProjections!.FindIndex(e => e.Equals(expression)); - if (existingIndex == -1) - { - _clientProjections.Add(expression); - existingIndex = _clientProjections.Count - 1; - } - - return new ProjectionBindingExpression(_queryExpression, existingIndex, type); - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryContext.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryContext.cs deleted file mode 100644 index a53856e2..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright 2023 MASES s.r.l. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* Refer to LICENSE for more information. -*/ - -using MASES.EntityFrameworkCore.KNet.Storage.Internal; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; -/// -/// 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. -/// -public class KafkaQueryContext : QueryContext -{ - private readonly IKafkaCluster _cluster; - /// - /// Retrieve for the specified - /// - /// - /// - public virtual IEnumerable GetValueBuffers(IEntityType entityType) - { - return _cluster.GetValueBuffers(entityType); - } - /// - /// Default initializer - /// - public KafkaQueryContext(QueryContextDependencies dependencies, IKafkaCluster cluster) - : base(dependencies) - { - _cluster = cluster; - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryContextFactory.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryContextFactory.cs deleted file mode 100644 index 20ce8d0b..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryContextFactory.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* -* Copyright 2023 MASES s.r.l. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* Refer to LICENSE for more information. -*/ - -using MASES.EntityFrameworkCore.KNet.Storage.Internal; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; -/// -/// 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. -/// -public class KafkaQueryContextFactory : IQueryContextFactory -{ - private readonly IKafkaCluster _cluster; - /// - /// Default initializer - /// - public KafkaQueryContextFactory( - QueryContextDependencies dependencies, - IKafkaClusterCache clusterCache, - IDbContextOptions contextOptions) - { - _cluster = clusterCache.GetCluster(contextOptions); - Dependencies = dependencies; - } - - /// - /// Dependencies for this service. - /// - protected virtual QueryContextDependencies Dependencies { get; } - /// - public virtual QueryContext Create() => new KafkaQueryContext(Dependencies, _cluster); -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.Helper.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.Helper.cs deleted file mode 100644 index 66a371a3..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.Helper.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Diagnostics.CodeAnalysis; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -public partial class KafkaQueryExpression -{ - private sealed class ResultEnumerable : IEnumerable - { - private readonly Func _getElement; - - public ResultEnumerable(Func getElement) - { - _getElement = getElement; - } - - public IEnumerator GetEnumerator() - => new ResultEnumerator(_getElement()); - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - - private sealed class ResultEnumerator : IEnumerator - { - private readonly ValueBuffer _value; - private bool _moved; - - public ResultEnumerator(ValueBuffer value) - { - _value = value; - _moved = _value.IsEmpty; - } - - public bool MoveNext() - { - if (!_moved) - { - _moved = true; - - return _moved; - } - - return false; - } - - public void Reset() - => _moved = false; - - object IEnumerator.Current - => Current; - - public ValueBuffer Current - => !_moved ? ValueBuffer.Empty : _value; - - void IDisposable.Dispose() - { - } - } - } - - private sealed class ProjectionMemberRemappingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _queryExpression; - private readonly Dictionary _projectionMemberMappings; - - public ProjectionMemberRemappingExpressionVisitor( - Expression queryExpression, - Dictionary projectionMemberMappings) - { - _queryExpression = queryExpression; - _projectionMemberMappings = projectionMemberMappings; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (expression is ProjectionBindingExpression projectionBindingExpression) - { - Check.DebugAssert( - projectionBindingExpression.ProjectionMember != null, - "ProjectionBindingExpression must have projection member."); - - return new ProjectionBindingExpression( - _queryExpression, - _projectionMemberMappings[projectionBindingExpression.ProjectionMember], - projectionBindingExpression.Type); - } - - return base.Visit(expression); - } - } - - private sealed class ProjectionMemberToIndexConvertingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _queryExpression; - private readonly Dictionary _projectionMemberMappings; - - public ProjectionMemberToIndexConvertingExpressionVisitor( - Expression queryExpression, - Dictionary projectionMemberMappings) - { - _queryExpression = queryExpression; - _projectionMemberMappings = projectionMemberMappings; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (expression is ProjectionBindingExpression projectionBindingExpression) - { - Check.DebugAssert( - projectionBindingExpression.ProjectionMember != null, - "ProjectionBindingExpression must have projection member."); - - return new ProjectionBindingExpression( - _queryExpression, - _projectionMemberMappings[projectionBindingExpression.ProjectionMember], - projectionBindingExpression.Type); - } - - return base.Visit(expression); - } - } - - private sealed class ProjectionIndexRemappingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _oldExpression; - private readonly Expression _newExpression; - private readonly int[] _indexMap; - - public ProjectionIndexRemappingExpressionVisitor( - Expression oldExpression, - Expression newExpression, - int[] indexMap) - { - _oldExpression = oldExpression; - _newExpression = newExpression; - _indexMap = indexMap; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (expression is ProjectionBindingExpression projectionBindingExpression - && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldExpression)) - { - Check.DebugAssert( - projectionBindingExpression.Index != null, - "ProjectionBindingExpression must have index."); - - return new ProjectionBindingExpression( - _newExpression, - _indexMap[projectionBindingExpression.Index.Value], - projectionBindingExpression.Type); - } - - return base.Visit(expression); - } - } - - private sealed class EntityShaperNullableMarkingExpressionVisitor : ExpressionVisitor - { - protected override Expression VisitExtension(Expression extensionExpression) - => extensionExpression is StructuralTypeShaperExpression shaper - ? shaper.MakeNullable() - : base.VisitExtension(extensionExpression); - } - - private sealed class QueryExpressionReplacingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _oldQuery; - private readonly Expression _newQuery; - - public QueryExpressionReplacingExpressionVisitor(Expression oldQuery, Expression newQuery) - { - _oldQuery = oldQuery; - _newQuery = newQuery; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - => expression is ProjectionBindingExpression projectionBindingExpression - && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldQuery) - ? projectionBindingExpression.ProjectionMember != null - ? new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.ProjectionMember!, projectionBindingExpression.Type) - : new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.Index!.Value, projectionBindingExpression.Type) - : base.Visit(expression); - } - - private sealed class CloningExpressionVisitor : ExpressionVisitor - { - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - if (expression is KafkaQueryExpression kafkaQueryExpression) - { - var clonedKafkaQueryExpression = new KafkaQueryExpression( - kafkaQueryExpression.ServerQueryExpression, kafkaQueryExpression._valueBufferParameter) - { - _groupingParameter = kafkaQueryExpression._groupingParameter, - _singleResultMethodInfo = kafkaQueryExpression._singleResultMethodInfo, - _scalarServerQuery = kafkaQueryExpression._scalarServerQuery - }; - - clonedKafkaQueryExpression._clientProjections.AddRange( - kafkaQueryExpression._clientProjections.Select(e => Visit(e))); - clonedKafkaQueryExpression._projectionMappingExpressions.AddRange( - kafkaQueryExpression._projectionMappingExpressions); - foreach (var (projectionMember, value) in kafkaQueryExpression._projectionMapping) - { - clonedKafkaQueryExpression._projectionMapping[projectionMember] = Visit(value); - } - - return clonedKafkaQueryExpression; - } - - if (expression is EntityProjectionExpression entityProjectionExpression) - { - return entityProjectionExpression.Clone(); - } - - return base.Visit(expression); - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.cs deleted file mode 100644 index f4042e62..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryExpression.cs +++ /dev/null @@ -1,1312 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using MASES.EntityFrameworkCore.KNet.Internal; -using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public partial class KafkaQueryExpression : Expression, IPrintableExpression -{ - private static readonly ConstructorInfo ValueBufferConstructor - = typeof(ValueBuffer).GetConstructors().Single(ci => ci.GetParameters().Length == 1); - - private static readonly PropertyInfo ValueBufferCountMemberInfo - = typeof(ValueBuffer).GetTypeInfo().GetProperty(nameof(ValueBuffer.Count))!; - - private static readonly MethodInfo LeftJoinMethodInfo = typeof(KafkaQueryExpression).GetTypeInfo() - .GetDeclaredMethods(nameof(LeftJoin)).Single(mi => mi.GetParameters().Length == 7); - - private static readonly ConstructorInfo ResultEnumerableConstructor - = typeof(ResultEnumerable).GetConstructors().Single(); - - private readonly ParameterExpression _valueBufferParameter; - private ParameterExpression? _groupingParameter; - private MethodInfo? _singleResultMethodInfo; - private bool _scalarServerQuery; - - private CloningExpressionVisitor? _cloningExpressionVisitor; - - private Dictionary _projectionMapping = new(); - private readonly List _clientProjections = new(); - private readonly List _projectionMappingExpressions = new(); - - private KafkaQueryExpression( - Expression serverQueryExpression, - ParameterExpression valueBufferParameter) - { - ServerQueryExpression = serverQueryExpression; - _valueBufferParameter = valueBufferParameter; - } - - /// - /// 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. - /// - public KafkaQueryExpression(IEntityType entityType) - { - _valueBufferParameter = Parameter(typeof(ValueBuffer), "valueBuffer"); - ServerQueryExpression = new KafkaTableExpression(entityType); - var propertyExpressionsMap = new Dictionary(); - var selectorExpressions = new List(); - foreach (var property in entityType.GetAllBaseTypesInclusive().SelectMany(et => et.GetDeclaredProperties())) - { - var propertyExpression = CreateReadValueExpression(property.ClrType, property.GetIndex(), property); - selectorExpressions.Add(propertyExpression); - - Check.DebugAssert( - property.GetIndex() == selectorExpressions.Count - 1, - "Properties should be ordered in same order as their indexes."); - propertyExpressionsMap[property] = propertyExpression; - _projectionMappingExpressions.Add(propertyExpression); - } - - var discriminatorProperty = entityType.FindDiscriminatorProperty(); - if (discriminatorProperty != null) - { - var keyValueComparer = discriminatorProperty.GetKeyValueComparer(); - foreach (var derivedEntityType in entityType.GetDerivedTypes()) - { - var entityCheck = derivedEntityType.GetConcreteDerivedTypesInclusive() - .Select( - e => keyValueComparer.ExtractEqualsBody( - propertyExpressionsMap[discriminatorProperty], - Constant(e.GetDiscriminatorValue(), discriminatorProperty.ClrType))) - .Aggregate((l, r) => OrElse(l, r)); - - foreach (var property in derivedEntityType.GetDeclaredProperties()) - { - // We read nullable value from property of derived type since it could be null. - var typeToRead = property.ClrType.MakeNullable(); - var propertyExpression = Condition( - entityCheck, - CreateReadValueExpression(typeToRead, property.GetIndex(), property), - Default(typeToRead)); - - selectorExpressions.Add(propertyExpression); - var readExpression = CreateReadValueExpression(propertyExpression.Type, selectorExpressions.Count - 1, property); - propertyExpressionsMap[property] = readExpression; - _projectionMappingExpressions.Add(readExpression); - } - } - - // Force a selector if entity projection has complex expressions. - var selectorLambda = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - selectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - CurrentParameter); - - ServerQueryExpression = Call( - EnumerableMethods.Select.MakeGenericMethod(typeof(ValueBuffer), typeof(ValueBuffer)), - ServerQueryExpression, - selectorLambda); - } - - var entityProjection = new EntityProjectionExpression(entityType, propertyExpressionsMap); - _projectionMapping[new ProjectionMember()] = entityProjection; - } - - /// - /// 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. - /// - public virtual Expression ServerQueryExpression { get; private set; } - - /// - /// 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. - /// - public virtual ParameterExpression CurrentParameter - => _groupingParameter ?? _valueBufferParameter; - - /// - /// 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. - /// - public virtual void ReplaceProjection(IReadOnlyList clientProjections) - { - _projectionMapping.Clear(); - _projectionMappingExpressions.Clear(); - _clientProjections.Clear(); - _clientProjections.AddRange(clientProjections); - } - - /// - /// 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. - /// - public virtual void ReplaceProjection(IReadOnlyDictionary projectionMapping) - { - _projectionMapping.Clear(); - _projectionMappingExpressions.Clear(); - _clientProjections.Clear(); - var selectorExpressions = new List(); - foreach (var (projectionMember, expression) in projectionMapping) - { - if (expression is EntityProjectionExpression entityProjectionExpression) - { - _projectionMapping[projectionMember] = AddEntityProjection(entityProjectionExpression); - } - else - { - selectorExpressions.Add(expression); - var readExpression = CreateReadValueExpression( - expression.Type, selectorExpressions.Count - 1, InferPropertyFromInner(expression)); - _projectionMapping[projectionMember] = readExpression; - _projectionMappingExpressions.Add(readExpression); - } - } - - if (selectorExpressions.Count == 0) - { - // No server correlated term in projection so add dummy 1. - selectorExpressions.Add(Constant(1)); - } - - var selectorLambda = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - selectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e).ToArray())), - CurrentParameter); - - ServerQueryExpression = Call( - EnumerableMethods.Select.MakeGenericMethod(CurrentParameter.Type, typeof(ValueBuffer)), - ServerQueryExpression, - selectorLambda); - - _groupingParameter = null; - - EntityProjectionExpression AddEntityProjection(EntityProjectionExpression entityProjectionExpression) - { - var readExpressionMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - var expression = entityProjectionExpression.BindProperty(property); - selectorExpressions.Add(expression); - var newExpression = CreateReadValueExpression(expression.Type, selectorExpressions.Count - 1, property); - readExpressionMap[property] = newExpression; - _projectionMappingExpressions.Add(newExpression); - } - - var result = new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressionMap); - - // Also compute nested entity projections - foreach (var navigation in entityProjectionExpression.EntityType.GetAllBaseTypes() - .Concat(entityProjectionExpression.EntityType.GetDerivedTypesInclusive()) - .SelectMany(t => t.GetDeclaredNavigations())) - { - var boundEntityShaperExpression = entityProjectionExpression.BindNavigation(navigation); - if (boundEntityShaperExpression != null) - { - var innerEntityProjection = (EntityProjectionExpression)boundEntityShaperExpression.ValueBufferExpression; - var newInnerEntityProjection = AddEntityProjection(innerEntityProjection); - boundEntityShaperExpression = boundEntityShaperExpression.Update(newInnerEntityProjection); - result.AddNavigationBinding(navigation, boundEntityShaperExpression); - } - } - - return result; - } - } - - /// - /// 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. - /// - public virtual Expression GetProjection(ProjectionBindingExpression projectionBindingExpression) - => projectionBindingExpression.ProjectionMember != null - ? _projectionMapping[projectionBindingExpression.ProjectionMember] - : _clientProjections[projectionBindingExpression.Index!.Value]; - - /// - /// 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. - /// - public virtual void ApplyProjection() - { - if (_scalarServerQuery) - { - _projectionMapping[new ProjectionMember()] = Constant(0); - return; - } - - var selectorExpressions = new List(); - if (_clientProjections.Count > 0) - { - for (var i = 0; i < _clientProjections.Count; i++) - { - var projection = _clientProjections[i]; - switch (projection) - { - case EntityProjectionExpression entityProjectionExpression: - { - var indexMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - selectorExpressions.Add(entityProjectionExpression.BindProperty(property)); - indexMap[property] = selectorExpressions.Count - 1; - } - - _clientProjections[i] = Constant(indexMap); - break; - } - - case KafkaQueryExpression kafkaQueryExpression: - { - var singleResult = kafkaQueryExpression._scalarServerQuery - || kafkaQueryExpression._singleResultMethodInfo != null; - kafkaQueryExpression.ApplyProjection(); - var serverQuery = kafkaQueryExpression.ServerQueryExpression; - if (singleResult) - { - serverQuery = ((LambdaExpression)((NewExpression)serverQuery).Arguments[0]).Body; - } - - selectorExpressions.Add(serverQuery); - _clientProjections[i] = Constant(selectorExpressions.Count - 1); - break; - } - - default: - selectorExpressions.Add(projection); - _clientProjections[i] = Constant(selectorExpressions.Count - 1); - break; - } - } - } - else - { - var newProjectionMapping = new Dictionary(); - foreach (var (projectionMember, expression) in _projectionMapping) - { - if (expression is EntityProjectionExpression entityProjectionExpression) - { - var indexMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - selectorExpressions.Add(entityProjectionExpression.BindProperty(property)); - indexMap[property] = selectorExpressions.Count - 1; - } - - newProjectionMapping[projectionMember] = Constant(indexMap); - } - else - { - selectorExpressions.Add(expression); - newProjectionMapping[projectionMember] = Constant(selectorExpressions.Count - 1); - } - } - - _projectionMapping = newProjectionMapping; - _projectionMappingExpressions.Clear(); - } - - var selectorLambda = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - selectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e).ToArray())), - CurrentParameter); - - ServerQueryExpression = Call( - EnumerableMethods.Select.MakeGenericMethod(CurrentParameter.Type, typeof(ValueBuffer)), - ServerQueryExpression, - selectorLambda); - - _groupingParameter = null; - - if (_singleResultMethodInfo != null) - { - ServerQueryExpression = Call( - _singleResultMethodInfo.MakeGenericMethod(CurrentParameter.Type), - ServerQueryExpression); - - ConvertToEnumerable(); - - _singleResultMethodInfo = null; - } - } - - /// - /// 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. - /// - public virtual void UpdateServerQueryExpression(Expression serverQueryExpression) - => ServerQueryExpression = serverQueryExpression; - - /// - /// 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. - /// - public virtual void ApplySetOperation(MethodInfo setOperationMethodInfo, KafkaQueryExpression source2) - { - Check.DebugAssert(_groupingParameter == null, "Cannot apply set operation after GroupBy without flattening."); - if (_clientProjections.Count == 0) - { - var projectionMapping = new Dictionary(); - var source1SelectorExpressions = new List(); - var source2SelectorExpressions = new List(); - foreach (var (key, value1, value2) in _projectionMapping.Join( - source2._projectionMapping, kv => kv.Key, kv => kv.Key, - (kv1, kv2) => (kv1.Key, Value1: kv1.Value, Value2: kv2.Value))) - { - if (value1 is EntityProjectionExpression entityProjection1 - && value2 is EntityProjectionExpression entityProjection2) - { - var map = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjection1.EntityType)) - { - var expressionToAdd1 = entityProjection1.BindProperty(property); - var expressionToAdd2 = entityProjection2.BindProperty(property); - source1SelectorExpressions.Add(expressionToAdd1); - source2SelectorExpressions.Add(expressionToAdd2); - var type = expressionToAdd1.Type; - if (!type.IsNullableType() - && expressionToAdd2.Type.IsNullableType()) - { - type = expressionToAdd2.Type; - } - - map[property] = CreateReadValueExpression(type, source1SelectorExpressions.Count - 1, property); - } - - projectionMapping[key] = new EntityProjectionExpression(entityProjection1.EntityType, map); - } - else - { - source1SelectorExpressions.Add(value1); - source2SelectorExpressions.Add(value2); - var type = value1.Type; - if (!type.IsNullableType() - && value2.Type.IsNullableType()) - { - type = value2.Type; - } - - projectionMapping[key] = CreateReadValueExpression( - type, source1SelectorExpressions.Count - 1, InferPropertyFromInner(value1)); - } - } - - _projectionMapping = projectionMapping; - - ServerQueryExpression = Call( - EnumerableMethods.Select.MakeGenericMethod(ServerQueryExpression.Type.GetSequenceType(), typeof(ValueBuffer)), - ServerQueryExpression, - Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - source1SelectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - CurrentParameter)); - - source2.ServerQueryExpression = Call( - EnumerableMethods.Select.MakeGenericMethod(source2.ServerQueryExpression.Type.GetSequenceType(), typeof(ValueBuffer)), - source2.ServerQueryExpression, - Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - source2SelectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - source2.CurrentParameter)); - } - else - { - throw new InvalidOperationException(KafkaStrings.SetOperationsNotAllowedAfterClientEvaluation); - } - - ServerQueryExpression = Call( - setOperationMethodInfo.MakeGenericMethod(typeof(ValueBuffer)), ServerQueryExpression, source2.ServerQueryExpression); - } - - /// - /// 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. - /// - public virtual void ApplyDefaultIfEmpty() - { - if (_clientProjections.Count != 0) - { - throw new InvalidOperationException(KafkaStrings.DefaultIfEmptyAppliedAfterProjection); - } - - var projectionMapping = new Dictionary(); - foreach (var (projectionMember, expression) in _projectionMapping) - { - projectionMapping[projectionMember] = expression is EntityProjectionExpression entityProjectionExpression - ? MakeEntityProjectionNullable(entityProjectionExpression) - : MakeReadValueNullable(expression); - } - - _projectionMapping = projectionMapping; - var projectionMappingExpressions = _projectionMappingExpressions.Select(e => MakeReadValueNullable(e)).ToList(); - _projectionMappingExpressions.Clear(); - _projectionMappingExpressions.AddRange(projectionMappingExpressions); - _groupingParameter = null; - - ServerQueryExpression = Call( - EnumerableMethods.DefaultIfEmptyWithArgument.MakeGenericMethod(typeof(ValueBuffer)), - ServerQueryExpression, - Constant(new ValueBuffer(Enumerable.Repeat((object?)null, _projectionMappingExpressions.Count).ToArray()))); - } - - /// - /// 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. - /// - public virtual void ApplyDistinct() - { - Check.DebugAssert(!_scalarServerQuery && _singleResultMethodInfo == null, "Cannot apply distinct on single result query"); - Check.DebugAssert(_groupingParameter == null, "Cannot apply distinct after GroupBy before flattening."); - - var selectorExpressions = new List(); - if (_clientProjections.Count == 0) - { - selectorExpressions.AddRange(_projectionMappingExpressions); - if (selectorExpressions.Count == 0) - { - // No server correlated term in projection so add dummy 1. - selectorExpressions.Add(Constant(1)); - } - } - else - { - for (var i = 0; i < _clientProjections.Count; i++) - { - var projection = _clientProjections[i]; - if (projection is KafkaQueryExpression) - { - throw new InvalidOperationException(KafkaStrings.DistinctOnSubqueryNotSupported); - } - - if (projection is EntityProjectionExpression entityProjectionExpression) - { - _clientProjections[i] = TraverseEntityProjection( - selectorExpressions, entityProjectionExpression, makeNullable: false); - } - else - { - selectorExpressions.Add(projection); - _clientProjections[i] = CreateReadValueExpression( - projection.Type, selectorExpressions.Count - 1, InferPropertyFromInner(projection)); - } - } - } - - var selectorLambda = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - selectorExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e).ToArray())), - CurrentParameter); - - ServerQueryExpression = Call( - EnumerableMethods.Distinct.MakeGenericMethod(typeof(ValueBuffer)), - Call( - EnumerableMethods.Select.MakeGenericMethod(CurrentParameter.Type, typeof(ValueBuffer)), - ServerQueryExpression, - selectorLambda)); - } - - /// - /// 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. - /// - public virtual GroupByShaperExpression ApplyGrouping( - Expression groupingKey, - Expression shaperExpression, - bool defaultElementSelector) - { - var source = ServerQueryExpression; - Expression? selector; - if (defaultElementSelector) - { - selector = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - _projectionMappingExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - _valueBufferParameter); - } - else - { - var selectMethodCall = (MethodCallExpression)ServerQueryExpression; - source = selectMethodCall.Arguments[0]; - selector = selectMethodCall.Arguments[1]; - } - - _groupingParameter = Parameter(typeof(IGrouping), "grouping"); - var groupingKeyAccessExpression = PropertyOrField(_groupingParameter, nameof(IGrouping.Key)); - var groupingKeyExpressions = new List(); - groupingKey = GetGroupingKey(groupingKey, groupingKeyExpressions, groupingKeyAccessExpression); - var keySelector = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - groupingKeyExpressions.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - _valueBufferParameter); - - ServerQueryExpression = Call( - EnumerableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), typeof(ValueBuffer)), - source, - keySelector, - selector); - - var clonedKafkaQueryExpression = Clone(); - clonedKafkaQueryExpression.UpdateServerQueryExpression(_groupingParameter); - clonedKafkaQueryExpression._groupingParameter = null; - - return new GroupByShaperExpression( - groupingKey, - new ShapedQueryExpression( - clonedKafkaQueryExpression, - new QueryExpressionReplacingExpressionVisitor(this, clonedKafkaQueryExpression).Visit(shaperExpression))); - } - - /// - /// 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. - /// - public virtual Expression AddInnerJoin( - KafkaQueryExpression innerQueryExpression, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector, - Expression outerShaperExpression, - Expression innerShaperExpression) - => AddJoin( - innerQueryExpression, outerKeySelector, innerKeySelector, outerShaperExpression, innerShaperExpression, - innerNullable: false); - - /// - /// 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. - /// - public virtual Expression AddLeftJoin( - KafkaQueryExpression innerQueryExpression, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector, - Expression outerShaperExpression, - Expression innerShaperExpression) - => AddJoin( - innerQueryExpression, outerKeySelector, innerKeySelector, outerShaperExpression, innerShaperExpression, - innerNullable: true); - - /// - /// 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. - /// - public virtual Expression AddSelectMany( - KafkaQueryExpression innerQueryExpression, - Expression outerShaperExpression, - Expression innerShaperExpression, - bool innerNullable) - => AddJoin(innerQueryExpression, null, null, outerShaperExpression, innerShaperExpression, innerNullable); - - /// - /// 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. - /// - public virtual StructuralTypeShaperExpression AddNavigationToWeakEntityType( - EntityProjectionExpression entityProjectionExpression, - INavigation navigation, - KafkaQueryExpression innerQueryExpression, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector) - { - Check.DebugAssert(_clientProjections.Count == 0, "Cannot expand weak entity navigation after client projection yet."); - var outerParameter = Parameter(typeof(ValueBuffer), "outer"); - var innerParameter = Parameter(typeof(ValueBuffer), "inner"); - var replacingVisitor = new ReplacingExpressionVisitor( - new Expression[] { CurrentParameter, innerQueryExpression.CurrentParameter }, - new Expression[] { outerParameter, innerParameter }); - - var selectorExpressions = _projectionMappingExpressions.Select(e => replacingVisitor.Visit(e)).ToList(); - var outerIndex = selectorExpressions.Count; - var innerEntityProjection = (EntityProjectionExpression)innerQueryExpression._projectionMapping[new ProjectionMember()]; - var innerReadExpressionMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(innerEntityProjection.EntityType)) - { - var propertyExpression = innerEntityProjection.BindProperty(property); - propertyExpression = MakeReadValueNullable(propertyExpression); - - selectorExpressions.Add(propertyExpression); - var readValueExpression = CreateReadValueExpression(propertyExpression.Type, selectorExpressions.Count - 1, property); - innerReadExpressionMap[property] = readValueExpression; - _projectionMappingExpressions.Add(readValueExpression); - } - - innerEntityProjection = new EntityProjectionExpression(innerEntityProjection.EntityType, innerReadExpressionMap); - - var resultSelector = Lambda( - New( - ValueBufferConstructor, - NewArrayInit( - typeof(object), - selectorExpressions - .Select(e => replacingVisitor.Visit(e)) - .Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))), - outerParameter, - innerParameter); - - ServerQueryExpression = Call( - LeftJoinMethodInfo.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), - ServerQueryExpression, - innerQueryExpression.ServerQueryExpression, - outerKeySelector, - innerKeySelector, - resultSelector, - Constant(new ValueBuffer(Enumerable.Repeat((object?)null, selectorExpressions.Count - outerIndex).ToArray())), - Constant(null, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); - - var entityShaper = new StructuralTypeShaperExpression(innerEntityProjection.EntityType, innerEntityProjection, nullable: true); - entityProjectionExpression.AddNavigationBinding(navigation, entityShaper); - - return entityShaper; - } - - /// - /// 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. - /// - public virtual ShapedQueryExpression Clone(Expression shaperExpression) - { - var clonedKafkaQueryExpression = Clone(); - - return new ShapedQueryExpression( - clonedKafkaQueryExpression, - new QueryExpressionReplacingExpressionVisitor(this, clonedKafkaQueryExpression).Visit(shaperExpression)); - } - - /// - /// 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. - /// - public virtual Expression GetSingleScalarProjection() - { - var expression = CreateReadValueExpression(ServerQueryExpression.Type, 0, null); - _projectionMapping.Clear(); - _projectionMappingExpressions.Clear(); - _clientProjections.Clear(); - _projectionMapping[new ProjectionMember()] = expression; - _projectionMappingExpressions.Add(expression); - _groupingParameter = null; - - _scalarServerQuery = true; - ConvertToEnumerable(); - - return new ProjectionBindingExpression(this, new ProjectionMember(), expression.Type.MakeNullable()); - } - - /// - /// 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. - /// - public virtual void ConvertToSingleResult(MethodInfo methodInfo) - => _singleResultMethodInfo = methodInfo; - - /// - /// 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. - /// - public override Type Type - => typeof(IEnumerable); - - /// - /// 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. - /// - public sealed override ExpressionType NodeType - => ExpressionType.Extension; - - /// - /// 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. - /// - void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) - { - expressionPrinter.AppendLine(nameof(KafkaQueryExpression) + ": "); - using (expressionPrinter.Indent()) - { - expressionPrinter.AppendLine(nameof(ServerQueryExpression) + ": "); - using (expressionPrinter.Indent()) - { - expressionPrinter.Visit(ServerQueryExpression); - } - - expressionPrinter.AppendLine(); - if (_clientProjections.Count > 0) - { - expressionPrinter.AppendLine("ClientProjections:"); - using (expressionPrinter.Indent()) - { - for (var i = 0; i < _clientProjections.Count; i++) - { - expressionPrinter.AppendLine(); - expressionPrinter.Append(i.ToString()).Append(" -> "); - expressionPrinter.Visit(_clientProjections[i]); - } - } - } - else - { - expressionPrinter.AppendLine("ProjectionMapping:"); - using (expressionPrinter.Indent()) - { - foreach (var (projectionMember, expression) in _projectionMapping) - { - expressionPrinter.Append("Member: " + projectionMember + " Projection: "); - expressionPrinter.Visit(expression); - expressionPrinter.AppendLine(","); - } - } - } - - expressionPrinter.AppendLine(); - } - } - - private KafkaQueryExpression Clone() - { - _cloningExpressionVisitor ??= new CloningExpressionVisitor(); - - return (KafkaQueryExpression)_cloningExpressionVisitor.Visit(this); - } - - private static Expression GetGroupingKey(Expression key, List groupingExpressions, Expression groupingKeyAccessExpression) - { - switch (key) - { - case NewExpression newExpression: - var arguments = new Expression[newExpression.Arguments.Count]; - for (var i = 0; i < arguments.Length; i++) - { - arguments[i] = GetGroupingKey(newExpression.Arguments[i], groupingExpressions, groupingKeyAccessExpression); - } - - return newExpression.Update(arguments); - - case MemberInitExpression memberInitExpression: - if (memberInitExpression.Bindings.Any(mb => mb is not MemberAssignment)) - { - goto default; - } - - var updatedNewExpression = (NewExpression)GetGroupingKey( - memberInitExpression.NewExpression, groupingExpressions, groupingKeyAccessExpression); - var memberBindings = new MemberAssignment[memberInitExpression.Bindings.Count]; - for (var i = 0; i < memberBindings.Length; i++) - { - var memberAssignment = (MemberAssignment)memberInitExpression.Bindings[i]; - memberBindings[i] = memberAssignment.Update( - GetGroupingKey( - memberAssignment.Expression, - groupingExpressions, - groupingKeyAccessExpression)); - } - - return memberInitExpression.Update(updatedNewExpression, memberBindings); - - case StructuralTypeShaperExpression { ValueBufferExpression: ProjectionBindingExpression projectionBindingExpression } shaper: - var entityProjectionExpression = - (EntityProjectionExpression)((KafkaQueryExpression)projectionBindingExpression.QueryExpression) - .GetProjection(projectionBindingExpression); - var readExpressions = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - readExpressions[property] = (MethodCallExpression)GetGroupingKey( - entityProjectionExpression.BindProperty(property), - groupingExpressions, - groupingKeyAccessExpression); - } - - return shaper.Update( - new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressions)); - - default: - var index = groupingExpressions.Count; - groupingExpressions.Add(key); - return groupingKeyAccessExpression.CreateValueBufferReadValueExpression( - key.Type, - index, - InferPropertyFromInner(key)); - } - } - - private Expression AddJoin( - KafkaQueryExpression innerQueryExpression, - LambdaExpression? outerKeySelector, - LambdaExpression? innerKeySelector, - Expression outerShaperExpression, - Expression innerShaperExpression, - bool innerNullable) - { - var transparentIdentifierType = TransparentIdentifierFactory.Create(outerShaperExpression.Type, innerShaperExpression.Type); - var outerMemberInfo = transparentIdentifierType.GetTypeInfo().GetDeclaredField("Outer")!; - var innerMemberInfo = transparentIdentifierType.GetTypeInfo().GetDeclaredField("Inner")!; - var outerClientEval = _clientProjections.Count > 0; - var innerClientEval = innerQueryExpression._clientProjections.Count > 0; - var resultSelectorExpressions = new List(); - var outerParameter = Parameter(typeof(ValueBuffer), "outer"); - var innerParameter = Parameter(typeof(ValueBuffer), "inner"); - var replacingVisitor = new ReplacingExpressionVisitor( - new Expression[] { CurrentParameter, innerQueryExpression.CurrentParameter }, - new Expression[] { outerParameter, innerParameter }); - int outerIndex; - - if (outerClientEval) - { - // Outer projection are already populated - if (innerClientEval) - { - // Add inner to projection and update indexes - var indexMap = new int[innerQueryExpression._clientProjections.Count]; - for (var i = 0; i < innerQueryExpression._clientProjections.Count; i++) - { - var projectionToAdd = innerQueryExpression._clientProjections[i]; - projectionToAdd = MakeNullable(projectionToAdd, innerNullable); - _clientProjections.Add(projectionToAdd); - indexMap[i] = _clientProjections.Count - 1; - } - - innerQueryExpression._clientProjections.Clear(); - - innerShaperExpression = - new ProjectionIndexRemappingExpressionVisitor(innerQueryExpression, this, indexMap).Visit(innerShaperExpression); - } - else - { - // Apply inner projection mapping and convert projection member binding to indexes - var mapping = ConvertProjectionMappingToClientProjections(innerQueryExpression._projectionMapping, innerNullable); - innerShaperExpression = - new ProjectionMemberToIndexConvertingExpressionVisitor(this, mapping).Visit(innerShaperExpression); - } - - // TODO: We still need to populate and generate result selector - // Further for a subquery in projection we may need to update correlation terms used inside it. - throw new NotImplementedException(); - } - - if (innerClientEval) - { - // Since inner projections are populated, we need to populate outer also - var mapping = ConvertProjectionMappingToClientProjections(_projectionMapping); - outerShaperExpression = new ProjectionMemberToIndexConvertingExpressionVisitor(this, mapping).Visit(outerShaperExpression); - - var indexMap = new int[innerQueryExpression._clientProjections.Count]; - for (var i = 0; i < innerQueryExpression._clientProjections.Count; i++) - { - var projectionToAdd = innerQueryExpression._clientProjections[i]; - projectionToAdd = MakeNullable(projectionToAdd, innerNullable); - _clientProjections.Add(projectionToAdd); - indexMap[i] = _clientProjections.Count - 1; - } - - innerQueryExpression._clientProjections.Clear(); - - innerShaperExpression = - new ProjectionIndexRemappingExpressionVisitor(innerQueryExpression, this, indexMap).Visit(innerShaperExpression); - // TODO: We still need to populate and generate result selector - // Further for a subquery in projection we may need to update correlation terms used inside it. - throw new NotImplementedException(); - } - else - { - var projectionMapping = new Dictionary(); - var mapping = new Dictionary(); - foreach (var (projectionMember, expression) in _projectionMapping) - { - var newProjectionMember = projectionMember.Prepend(outerMemberInfo); - mapping[projectionMember] = newProjectionMember; - if (expression is EntityProjectionExpression entityProjectionExpression) - { - projectionMapping[newProjectionMember] = TraverseEntityProjection( - resultSelectorExpressions, entityProjectionExpression, makeNullable: false); - } - else - { - resultSelectorExpressions.Add(expression); - projectionMapping[newProjectionMember] = CreateReadValueExpression( - expression.Type, resultSelectorExpressions.Count - 1, InferPropertyFromInner(expression)); - } - } - - outerShaperExpression = new ProjectionMemberRemappingExpressionVisitor(this, mapping).Visit(outerShaperExpression); - mapping.Clear(); - - outerIndex = resultSelectorExpressions.Count; - foreach (var projection in innerQueryExpression._projectionMapping) - { - var newProjectionMember = projection.Key.Prepend(innerMemberInfo); - mapping[projection.Key] = newProjectionMember; - if (projection.Value is EntityProjectionExpression entityProjectionExpression) - { - projectionMapping[newProjectionMember] = TraverseEntityProjection( - resultSelectorExpressions, entityProjectionExpression, innerNullable); - } - else - { - var expression = projection.Value; - if (innerNullable) - { - expression = MakeReadValueNullable(expression); - } - - resultSelectorExpressions.Add(expression); - projectionMapping[newProjectionMember] = CreateReadValueExpression( - expression.Type, resultSelectorExpressions.Count - 1, InferPropertyFromInner(projection.Value)); - } - } - - innerShaperExpression = new ProjectionMemberRemappingExpressionVisitor(this, mapping).Visit(innerShaperExpression); - mapping.Clear(); - - _projectionMapping = projectionMapping; - } - - var resultSelector = Lambda( - New( - ValueBufferConstructor, NewArrayInit( - typeof(object), - resultSelectorExpressions.Select( - (e, i) => - { - var expression = replacingVisitor.Visit(e); - if (innerNullable - && i > outerIndex) - { - expression = MakeReadValueNullable(expression); - } - - if (expression.Type.IsValueType) - { - expression = Convert(expression, typeof(object)); - } - - return expression; - }))), - outerParameter, - innerParameter); - - if (outerKeySelector != null - && innerKeySelector != null) - { - var comparer = ((InferPropertyFromInner(outerKeySelector.Body) - ?? InferPropertyFromInner(outerKeySelector.Body)) - as IProperty)?.GetValueComparer(); - - if (comparer?.Type != outerKeySelector.ReturnType) - { - comparer = null; - } - - if (innerNullable) - { - ServerQueryExpression = Call( - LeftJoinMethodInfo.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), - ServerQueryExpression, - innerQueryExpression.ServerQueryExpression, - outerKeySelector, - innerKeySelector, - resultSelector, - Constant(new ValueBuffer(Enumerable.Repeat((object?)null, resultSelectorExpressions.Count - outerIndex).ToArray())), - Constant(comparer, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); - } - else - { - ServerQueryExpression = comparer == null - ? Call( - EnumerableMethods.Join.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), - ServerQueryExpression, - innerQueryExpression.ServerQueryExpression, - outerKeySelector, - innerKeySelector, - resultSelector) - : Call( - EnumerableMethods.JoinWithComparer.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)), - ServerQueryExpression, - innerQueryExpression.ServerQueryExpression, - outerKeySelector, - innerKeySelector, - resultSelector, - Constant(comparer, typeof(IEqualityComparer<>).MakeGenericType(outerKeySelector.ReturnType))); - } - } - else - { - // inner nullable should do something different here - // Issue#17536 - ServerQueryExpression = Call( - EnumerableMethods.SelectManyWithCollectionSelector.MakeGenericMethod( - typeof(ValueBuffer), typeof(ValueBuffer), typeof(ValueBuffer)), - ServerQueryExpression, - Lambda(innerQueryExpression.ServerQueryExpression, CurrentParameter), - resultSelector); - } - - if (innerNullable) - { - innerShaperExpression = new EntityShaperNullableMarkingExpressionVisitor().Visit(innerShaperExpression); - } - - return New( - transparentIdentifierType.GetTypeInfo().DeclaredConstructors.Single(), - new[] { outerShaperExpression, innerShaperExpression }, outerMemberInfo, innerMemberInfo); - - static Expression MakeNullable(Expression expression, bool nullable) - => nullable - ? expression is EntityProjectionExpression entityProjection - ? MakeEntityProjectionNullable(entityProjection) - : MakeReadValueNullable(expression) - : expression; - } - - private void ConvertToEnumerable() - { - if (_scalarServerQuery || _singleResultMethodInfo != null) - { - if (ServerQueryExpression.Type != typeof(ValueBuffer)) - { - if (ServerQueryExpression.Type.IsValueType) - { - ServerQueryExpression = Convert(ServerQueryExpression, typeof(object)); - } - - ServerQueryExpression = New( - ResultEnumerableConstructor, - Lambda>( - New( - ValueBufferConstructor, - NewArrayInit(typeof(object), ServerQueryExpression)))); - } - else - { - ServerQueryExpression = New( - ResultEnumerableConstructor, - Lambda>(ServerQueryExpression)); - } - } - } - - private MethodCallExpression CreateReadValueExpression(Type type, int index, IPropertyBase? property) - => (MethodCallExpression)_valueBufferParameter.CreateValueBufferReadValueExpression(type, index, property); - - private static IEnumerable GetAllPropertiesInHierarchy(IEntityType entityType) - => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) - .SelectMany(t => t.GetDeclaredProperties()); - - private static IPropertyBase? InferPropertyFromInner(Expression expression) - => expression is MethodCallExpression { Method.IsGenericMethod: true } methodCallExpression - && methodCallExpression.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod - ? methodCallExpression.Arguments[2].GetConstantValue() - : null; - - private static EntityProjectionExpression MakeEntityProjectionNullable(EntityProjectionExpression entityProjectionExpression) - { - var readExpressionMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - readExpressionMap[property] = MakeReadValueNullable(entityProjectionExpression.BindProperty(property)); - } - - var result = new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressionMap); - - // Also compute nested entity projections - foreach (var navigation in entityProjectionExpression.EntityType.GetAllBaseTypes() - .Concat(entityProjectionExpression.EntityType.GetDerivedTypesInclusive()) - .SelectMany(t => t.GetDeclaredNavigations())) - { - var boundEntityShaperExpression = entityProjectionExpression.BindNavigation(navigation); - if (boundEntityShaperExpression != null) - { - var innerEntityProjection = (EntityProjectionExpression)boundEntityShaperExpression.ValueBufferExpression; - var newInnerEntityProjection = MakeEntityProjectionNullable(innerEntityProjection); - boundEntityShaperExpression = boundEntityShaperExpression.Update(newInnerEntityProjection); - result.AddNavigationBinding(navigation, boundEntityShaperExpression); - } - } - - return result; - } - - private Dictionary ConvertProjectionMappingToClientProjections( - Dictionary projectionMapping, - bool makeNullable = false) - { - var mapping = new Dictionary(); - var entityProjectionCache = new Dictionary(ReferenceEqualityComparer.Instance); - foreach (var projection in projectionMapping) - { - var projectionMember = projection.Key; - var projectionToAdd = projection.Value; - - if (projectionToAdd is EntityProjectionExpression entityProjection) - { - if (!entityProjectionCache.TryGetValue(entityProjection, out var value)) - { - var entityProjectionToCache = entityProjection; - if (makeNullable) - { - entityProjection = MakeEntityProjectionNullable(entityProjection); - } - - _clientProjections.Add(entityProjection); - value = _clientProjections.Count - 1; - entityProjectionCache[entityProjectionToCache] = value; - } - - mapping[projectionMember] = value; - } - else - { - if (makeNullable) - { - projectionToAdd = MakeReadValueNullable(projectionToAdd); - } - - var existingIndex = _clientProjections.FindIndex(e => e.Equals(projectionToAdd)); - if (existingIndex == -1) - { - _clientProjections.Add(projectionToAdd); - existingIndex = _clientProjections.Count - 1; - } - - mapping[projectionMember] = existingIndex; - } - } - - projectionMapping.Clear(); - - return mapping; - } - - private static IEnumerable LeftJoin( - IEnumerable outer, - IEnumerable inner, - Func outerKeySelector, - Func innerKeySelector, - Func resultSelector, - TInner defaultValue, - IEqualityComparer? comparer) - => (comparer == null - ? outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (oe, ies) => new { oe, ies }) - : outer.GroupJoin(inner, outerKeySelector, innerKeySelector, (oe, ies) => new { oe, ies }, comparer)) - .SelectMany(t => t.ies.DefaultIfEmpty(defaultValue), (t, i) => resultSelector(t.oe, i)); - - private static MethodCallExpression MakeReadValueNullable(Expression expression) - { - Check.DebugAssert(expression is MethodCallExpression, "Expression must be method call expression."); - - var methodCallExpression = (MethodCallExpression)expression; - - return methodCallExpression.Type.IsNullableType() - ? methodCallExpression - : Call( - ExpressionExtensions.ValueBufferTryReadValueMethod.MakeGenericMethod(methodCallExpression.Type.MakeNullable()), - methodCallExpression.Arguments); - } - - private EntityProjectionExpression TraverseEntityProjection( - List selectorExpressions, - EntityProjectionExpression entityProjectionExpression, - bool makeNullable) - { - var readExpressionMap = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType)) - { - var expression = entityProjectionExpression.BindProperty(property); - if (makeNullable) - { - expression = MakeReadValueNullable(expression); - } - - selectorExpressions.Add(expression); - var newExpression = CreateReadValueExpression(expression.Type, selectorExpressions.Count - 1, property); - readExpressionMap[property] = newExpression; - } - - var result = new EntityProjectionExpression(entityProjectionExpression.EntityType, readExpressionMap); - - // Also compute nested entity projections - foreach (var navigation in entityProjectionExpression.EntityType.GetAllBaseTypes() - .Concat(entityProjectionExpression.EntityType.GetDerivedTypesInclusive()) - .SelectMany(t => t.GetDeclaredNavigations())) - { - var boundEntityShaperExpression = entityProjectionExpression.BindNavigation(navigation); - if (boundEntityShaperExpression != null) - { - var innerEntityProjection = (EntityProjectionExpression)boundEntityShaperExpression.ValueBufferExpression; - var newInnerEntityProjection = TraverseEntityProjection(selectorExpressions, innerEntityProjection, makeNullable); - boundEntityShaperExpression = boundEntityShaperExpression.Update(newInnerEntityProjection); - result.AddNavigationBinding(navigation, boundEntityShaperExpression); - } - } - - return result; - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitor.cs deleted file mode 100644 index 758b96df..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitor.cs +++ /dev/null @@ -1,1476 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaQueryableMethodTranslatingExpressionVisitor : QueryableMethodTranslatingExpressionVisitor -{ - private readonly KafkaExpressionTranslatingExpressionVisitor _expressionTranslator; - private readonly SharedTypeEntityExpandingExpressionVisitor _weakEntityExpandingExpressionVisitor; - private readonly KafkaProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor; - private readonly IModel _model; - - /// - /// 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. - /// - public KafkaQueryableMethodTranslatingExpressionVisitor( - QueryableMethodTranslatingExpressionVisitorDependencies dependencies, - QueryCompilationContext queryCompilationContext) - : base(dependencies, queryCompilationContext, subquery: false) - { - _expressionTranslator = new KafkaExpressionTranslatingExpressionVisitor(queryCompilationContext, this); - _weakEntityExpandingExpressionVisitor = new SharedTypeEntityExpandingExpressionVisitor(_expressionTranslator); - _projectionBindingExpressionVisitor = new KafkaProjectionBindingExpressionVisitor(this, _expressionTranslator); - _model = queryCompilationContext.Model; - } - - /// - /// 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. - /// - protected KafkaQueryableMethodTranslatingExpressionVisitor( - KafkaQueryableMethodTranslatingExpressionVisitor parentVisitor) - : base(parentVisitor.Dependencies, parentVisitor.QueryCompilationContext, subquery: true) - { - _expressionTranslator = new KafkaExpressionTranslatingExpressionVisitor(QueryCompilationContext, parentVisitor); - _weakEntityExpandingExpressionVisitor = new SharedTypeEntityExpandingExpressionVisitor(_expressionTranslator); - _projectionBindingExpressionVisitor = new KafkaProjectionBindingExpressionVisitor(this, _expressionTranslator); - _model = parentVisitor._model; - } - - /// - /// 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. - /// - protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVisitor() - => new KafkaQueryableMethodTranslatingExpressionVisitor(this); - - /// - /// 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. - /// - protected override Expression VisitExtension(Expression extensionExpression) - { - switch (extensionExpression) - { - case GroupByShaperExpression groupByShaperExpression: - var groupShapedQueryExpression = groupByShaperExpression.GroupingEnumerable; - - return ((KafkaQueryExpression)groupShapedQueryExpression.QueryExpression) - .Clone(groupShapedQueryExpression.ShaperExpression); - - case ShapedQueryExpression shapedQueryExpression: - return ((KafkaQueryExpression)shapedQueryExpression.QueryExpression) - .Clone(shapedQueryExpression.ShaperExpression); - - default: - return base.VisitExtension(extensionExpression); - } - } - - /// - /// 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. - /// - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Arguments.Count == 1 - && methodCallExpression.Arguments[0].Type.TryGetSequenceType() != null - && (string.Equals(methodCallExpression.Method.Name, "AsSplitQuery", StringComparison.Ordinal) - || string.Equals(methodCallExpression.Method.Name, "AsSingleQuery", StringComparison.Ordinal))) - { - return Visit(methodCallExpression.Arguments[0]); - } - - return base.VisitMethodCall(methodCallExpression); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) - => CreateShapedQueryExpressionStatic(entityType); - - private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityType entityType) - { - var queryExpression = new KafkaQueryExpression(entityType); - - return new ShapedQueryExpression( - queryExpression, - new StructuralTypeShaperExpression( - entityType, - new ProjectionBindingExpression( - queryExpression, - new ProjectionMember(), - typeof(ValueBuffer)), - false)); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateAll(ShapedQueryExpression source, LambdaExpression predicate) - { - predicate = Expression.Lambda(Expression.Not(predicate.Body), predicate.Parameters); - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - return null; - } - - source = newSource; - - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - if (source.ShaperExpression is GroupByShaperExpression) - { - kafkaQueryExpression.ReplaceProjection(new Dictionary()); - } - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Not( - Expression.Call( - EnumerableMethods.AnyWithoutPredicate.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression))); - - return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(bool))); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateAny(ShapedQueryExpression source, LambdaExpression? predicate) - { - if (predicate != null) - { - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - return null; - } - - source = newSource; - } - - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - if (source.ShaperExpression is GroupByShaperExpression) - { - kafkaQueryExpression.ReplaceProjection(new Dictionary()); - } - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.AnyWithoutPredicate.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression)); - - return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(bool))); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateAverage( - ShapedQueryExpression source, - LambdaExpression? selector, - Type resultType) - => TranslateScalarAggregate(source, selector, nameof(Enumerable.Average), resultType); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateCast(ShapedQueryExpression source, Type resultType) - => source.ShaperExpression.Type != resultType - ? source.UpdateShaperExpression(Expression.Convert(source.ShaperExpression, resultType)) - : source; - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2) - => TranslateSetOperation(EnumerableMethods.Concat, source1, source2); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateContains(ShapedQueryExpression source, Expression item) - { - var anyLambdaParameter = Expression.Parameter(item.Type, "p"); - var anyLambda = Expression.Lambda( - ExpressionExtensions.CreateEqualsExpression(anyLambdaParameter, item), - anyLambdaParameter); - - return TranslateAny(source, anyLambda); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateCount(ShapedQueryExpression source, LambdaExpression? predicate) - { - if (predicate != null) - { - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - return null; - } - - source = newSource; - } - - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - if (source.ShaperExpression is GroupByShaperExpression) - { - kafkaQueryExpression.ReplaceProjection(new Dictionary()); - } - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.CountWithoutPredicate.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression)); - - return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(int))); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateDefaultIfEmpty(ShapedQueryExpression source, Expression? defaultValue) - { - if (defaultValue == null) - { - ((KafkaQueryExpression)source.QueryExpression).ApplyDefaultIfEmpty(); - return source.UpdateShaperExpression(MarkShaperNullable(source.ShaperExpression)); - } - - return null; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateDistinct(ShapedQueryExpression source) - { - ((KafkaQueryExpression)source.QueryExpression).ApplyDistinct(); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateElementAtOrDefault( - ShapedQueryExpression source, - Expression index, - bool returnDefault) - => null; - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2) - => TranslateSetOperation(EnumerableMethods.Except, source1, source2); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateFirstOrDefault( - ShapedQueryExpression source, - LambdaExpression? predicate, - Type returnType, - bool returnDefault) - => TranslateSingleResultOperator( - source, - predicate, - returnType, - returnDefault - ? EnumerableMethods.FirstOrDefaultWithoutPredicate - : EnumerableMethods.FirstWithoutPredicate); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateGroupBy( - ShapedQueryExpression source, - LambdaExpression keySelector, - LambdaExpression? elementSelector, - LambdaExpression? resultSelector) - { - var remappedKeySelector = RemapLambdaBody(source, keySelector); - - var translatedKey = TranslateGroupingKey(remappedKeySelector); - if (translatedKey != null) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var defaultElementSelector = elementSelector == null || elementSelector.Body == elementSelector.Parameters[0]; - if (!defaultElementSelector) - { - source = TranslateSelect(source, elementSelector!); - } - - var groupByShaper = kafkaQueryExpression.ApplyGrouping(translatedKey, source.ShaperExpression, defaultElementSelector); - - if (resultSelector == null) - { - return source.UpdateShaperExpression(groupByShaper); - } - - var original1 = resultSelector.Parameters[0]; - var original2 = resultSelector.Parameters[1]; - - var newResultSelectorBody = new ReplacingExpressionVisitor( - new Expression[] { original1, original2 }, - new[] { groupByShaper.KeySelector, groupByShaper }).Visit(resultSelector.Body); - - newResultSelectorBody = ExpandSharedTypeEntities(kafkaQueryExpression, newResultSelectorBody); - var newShaper = _projectionBindingExpressionVisitor.Translate(kafkaQueryExpression, newResultSelectorBody); - - return source.UpdateShaperExpression(newShaper); - } - - return null; - } - - private Expression? TranslateGroupingKey(Expression expression) - { - switch (expression) - { - case NewExpression newExpression: - if (newExpression.Arguments.Count == 0) - { - return newExpression; - } - - var newArguments = new Expression[newExpression.Arguments.Count]; - for (var i = 0; i < newArguments.Length; i++) - { - var key = TranslateGroupingKey(newExpression.Arguments[i]); - if (key == null) - { - return null; - } - - newArguments[i] = key; - } - - return newExpression.Update(newArguments); - - case MemberInitExpression memberInitExpression: - var updatedNewExpression = (NewExpression?)TranslateGroupingKey(memberInitExpression.NewExpression); - if (updatedNewExpression == null) - { - return null; - } - - var newBindings = new MemberAssignment[memberInitExpression.Bindings.Count]; - for (var i = 0; i < newBindings.Length; i++) - { - var memberAssignment = (MemberAssignment)memberInitExpression.Bindings[i]; - var visitedExpression = TranslateGroupingKey(memberAssignment.Expression); - if (visitedExpression == null) - { - return null; - } - - newBindings[i] = memberAssignment.Update(visitedExpression); - } - - return memberInitExpression.Update(updatedNewExpression, newBindings); - - case StructuralTypeShaperExpression { ValueBufferExpression: ProjectionBindingExpression } shaper: - return shaper; - - default: - var translation = TranslateExpression(expression); - if (translation == null) - { - return null; - } - - return translation.Type == expression.Type - ? translation - : Expression.Convert(translation, expression.Type); - } - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateGroupJoin( - ShapedQueryExpression outer, - ShapedQueryExpression inner, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector, - LambdaExpression resultSelector) - => null; - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateIntersect(ShapedQueryExpression source1, ShapedQueryExpression source2) - => TranslateSetOperation(EnumerableMethods.Intersect, source1, source2); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateJoin( - ShapedQueryExpression outer, - ShapedQueryExpression inner, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector, - LambdaExpression resultSelector) - { - var (newOuterKeySelector, newInnerKeySelector) = ProcessJoinKeySelector(outer, inner, outerKeySelector, innerKeySelector); - - if (newOuterKeySelector == null - || newInnerKeySelector == null) - { - return null; - } - - (outerKeySelector, innerKeySelector) = (newOuterKeySelector, newInnerKeySelector); - - var outerShaperExpression = ((KafkaQueryExpression)outer.QueryExpression).AddInnerJoin( - (KafkaQueryExpression)inner.QueryExpression, - outerKeySelector, - innerKeySelector, - outer.ShaperExpression, - inner.ShaperExpression); - - outer = outer.UpdateShaperExpression(outerShaperExpression); - - return TranslateTwoParameterSelector(outer, resultSelector); - } - - private (LambdaExpression? OuterKeySelector, LambdaExpression? InnerKeySelector) ProcessJoinKeySelector( - ShapedQueryExpression outer, - ShapedQueryExpression inner, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector) - { - var left = RemapLambdaBody(outer, outerKeySelector); - var right = RemapLambdaBody(inner, innerKeySelector); - - var joinCondition = TranslateExpression(ExpressionExtensions.CreateEqualsExpression(left, right)); - - var (outerKeyBody, innerKeyBody) = DecomposeJoinCondition(joinCondition); - - if (outerKeyBody == null - || innerKeyBody == null) - { - return (null, null); - } - - outerKeySelector = Expression.Lambda(outerKeyBody, ((KafkaQueryExpression)outer.QueryExpression).CurrentParameter); - innerKeySelector = Expression.Lambda(innerKeyBody, ((KafkaQueryExpression)inner.QueryExpression).CurrentParameter); - - return AlignKeySelectorTypes(outerKeySelector, innerKeySelector); - } - - private static (Expression?, Expression?) DecomposeJoinCondition(Expression? joinCondition) - { - var leftExpressions = new List(); - var rightExpressions = new List(); - - return ProcessJoinCondition(joinCondition, leftExpressions, rightExpressions) - ? leftExpressions.Count == 1 - ? (leftExpressions[0], rightExpressions[0]) - : (CreateAnonymousObject(leftExpressions), CreateAnonymousObject(rightExpressions)) - : (null, null); - - // Kafka joins need to use AnonymousObject to perform correct key comparison for server side joins - static Expression CreateAnonymousObject(List expressions) - => Expression.New( - AnonymousObject.AnonymousObjectCtor, - Expression.NewArrayInit( - typeof(object), - expressions.Select(e => Expression.Convert(e, typeof(object))))); - } - - private static bool ProcessJoinCondition( - Expression? joinCondition, - List leftExpressions, - List rightExpressions) - { - if (joinCondition is BinaryExpression binaryExpression) - { - if (binaryExpression.NodeType == ExpressionType.Equal) - { - leftExpressions.Add(binaryExpression.Left); - rightExpressions.Add(binaryExpression.Right); - - return true; - } - - if (binaryExpression.NodeType == ExpressionType.AndAlso) - { - return ProcessJoinCondition(binaryExpression.Left, leftExpressions, rightExpressions) - && ProcessJoinCondition(binaryExpression.Right, leftExpressions, rightExpressions); - } - } - - if (joinCondition is MethodCallExpression { Method.Name: nameof(object.Equals), Arguments.Count: 2 } methodCallExpression - && ((methodCallExpression.Method.IsStatic - && methodCallExpression.Method.DeclaringType == typeof(object)) - || typeof(ValueComparer).IsAssignableFrom(methodCallExpression.Method.DeclaringType))) - { - leftExpressions.Add(methodCallExpression.Arguments[0]); - rightExpressions.Add(methodCallExpression.Arguments[1]); - - return true; - } - - return false; - } - - private static (LambdaExpression OuterKeySelector, LambdaExpression InnerKeySelector) - AlignKeySelectorTypes(LambdaExpression outerKeySelector, LambdaExpression innerKeySelector) - { - if (outerKeySelector.Body.Type != innerKeySelector.Body.Type) - { - if (IsConvertedToNullable(outerKeySelector.Body, innerKeySelector.Body)) - { - innerKeySelector = Expression.Lambda( - Expression.Convert(innerKeySelector.Body, outerKeySelector.Body.Type), innerKeySelector.Parameters); - } - else if (IsConvertedToNullable(innerKeySelector.Body, outerKeySelector.Body)) - { - outerKeySelector = Expression.Lambda( - Expression.Convert(outerKeySelector.Body, innerKeySelector.Body.Type), outerKeySelector.Parameters); - } - } - - return (outerKeySelector, innerKeySelector); - - static bool IsConvertedToNullable(Expression outer, Expression inner) - => outer.Type.IsNullableType() - && !inner.Type.IsNullableType() - && outer.Type.UnwrapNullableType() == inner.Type; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateLastOrDefault( - ShapedQueryExpression source, - LambdaExpression? predicate, - Type returnType, - bool returnDefault) - => TranslateSingleResultOperator( - source, - predicate, - returnType, - returnDefault - ? EnumerableMethods.LastOrDefaultWithoutPredicate - : EnumerableMethods.LastWithoutPredicate); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateLeftJoin( - ShapedQueryExpression outer, - ShapedQueryExpression inner, - LambdaExpression outerKeySelector, - LambdaExpression innerKeySelector, - LambdaExpression resultSelector) - { - var (newOuterKeySelector, newInnerKeySelector) = ProcessJoinKeySelector(outer, inner, outerKeySelector, innerKeySelector); - - if (newOuterKeySelector == null - || newInnerKeySelector == null) - { - return null; - } - - (outerKeySelector, innerKeySelector) = (newOuterKeySelector, newInnerKeySelector); - - var outerShaperExpression = ((KafkaQueryExpression)outer.QueryExpression).AddLeftJoin( - (KafkaQueryExpression)inner.QueryExpression, - outerKeySelector, - innerKeySelector, - outer.ShaperExpression, - inner.ShaperExpression); - - outer = outer.UpdateShaperExpression(outerShaperExpression); - - return TranslateTwoParameterSelector(outer, resultSelector); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateLongCount(ShapedQueryExpression source, LambdaExpression? predicate) - { - if (predicate != null) - { - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - return null; - } - - source = newSource; - } - - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - if (source.ShaperExpression is GroupByShaperExpression) - { - kafkaQueryExpression.ReplaceProjection(new Dictionary()); - } - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.LongCountWithoutPredicate.MakeGenericMethod( - kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression)); - - return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), typeof(long))); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateMax( - ShapedQueryExpression source, - LambdaExpression? selector, - Type resultType) - => TranslateScalarAggregate(source, selector, nameof(Enumerable.Max), resultType); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateMin(ShapedQueryExpression source, LambdaExpression? selector, Type resultType) - => TranslateScalarAggregate(source, selector, nameof(Enumerable.Min), resultType); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateOfType(ShapedQueryExpression source, Type resultType) - { - if (source.ShaperExpression is StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper) - { - if (entityType.ClrType == resultType) - { - return source; - } - - var parameterExpression = Expression.Parameter(shaper.Type); - var predicate = Expression.Lambda(Expression.TypeIs(parameterExpression, resultType), parameterExpression); - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - // EntityType is not part of hierarchy - return null; - } - - source = newSource; - - var baseType = entityType.GetAllBaseTypes().SingleOrDefault(et => et.ClrType == resultType); - if (baseType != null) - { - return source.UpdateShaperExpression(shaper.WithType(baseType)); - } - - var derivedType = entityType.GetDerivedTypes().Single(et => et.ClrType == resultType); - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - var projectionBindingExpression = (ProjectionBindingExpression)shaper.ValueBufferExpression; - var projectionMember = projectionBindingExpression.ProjectionMember; - Check.DebugAssert(new ProjectionMember().Equals(projectionMember), "Invalid ProjectionMember when processing OfType"); - - var entityProjectionExpression = - (EntityProjectionExpression)kafkaQueryExpression.GetProjection(projectionBindingExpression); - kafkaQueryExpression.ReplaceProjection( - new Dictionary - { - { projectionMember, entityProjectionExpression.UpdateEntityType(derivedType) } - }); - - return source.UpdateShaperExpression(shaper.WithType(derivedType)); - } - - return null; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateOrderBy( - ShapedQueryExpression source, - LambdaExpression keySelector, - bool ascending) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - var newKeySelector = TranslateLambdaExpression(source, keySelector); - if (newKeySelector == null) - { - return null; - } - - keySelector = newKeySelector; - - var orderBy = ascending ? EnumerableMethods.OrderBy : EnumerableMethods.OrderByDescending; - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - orderBy.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type, keySelector.ReturnType), - kafkaQueryExpression.ServerQueryExpression, - keySelector)); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateReverse(ShapedQueryExpression source) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.Reverse.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression)); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression source, LambdaExpression selector) - { - if (selector.Body == selector.Parameters[0]) - { - return source; - } - - var newSelectorBody = RemapLambdaBody(source, selector); - var queryExpression = (KafkaQueryExpression)source.QueryExpression; - var newShaper = _projectionBindingExpressionVisitor.Translate(queryExpression, newSelectorBody); - - return source.UpdateShaperExpression(newShaper); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSelectMany( - ShapedQueryExpression source, - LambdaExpression collectionSelector, - LambdaExpression resultSelector) - { - var defaultIfEmpty = new DefaultIfEmptyFindingExpressionVisitor().IsOptional(collectionSelector); - var collectionSelectorBody = RemapLambdaBody(source, collectionSelector); - - if (Visit(collectionSelectorBody) is ShapedQueryExpression inner) - { - var outerShaperExpression = ((KafkaQueryExpression)source.QueryExpression).AddSelectMany( - (KafkaQueryExpression)inner.QueryExpression, source.ShaperExpression, inner.ShaperExpression, defaultIfEmpty); - - source = source.UpdateShaperExpression(outerShaperExpression); - - return TranslateTwoParameterSelector(source, resultSelector); - } - - return null; - } - - private sealed class DefaultIfEmptyFindingExpressionVisitor : ExpressionVisitor - { - private bool _defaultIfEmpty; - - public bool IsOptional(LambdaExpression lambdaExpression) - { - _defaultIfEmpty = false; - - Visit(lambdaExpression.Body); - - return _defaultIfEmpty; - } - - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.DefaultIfEmptyWithoutArgument) - { - _defaultIfEmpty = true; - } - - return base.VisitMethodCall(methodCallExpression); - } - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSelectMany(ShapedQueryExpression source, LambdaExpression selector) - { - var innerParameter = Expression.Parameter(selector.ReturnType.GetSequenceType(), "i"); - var resultSelector = Expression.Lambda( - innerParameter, Expression.Parameter(source.Type.GetSequenceType()), innerParameter); - - return TranslateSelectMany(source, selector, resultSelector); - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSingleOrDefault( - ShapedQueryExpression source, - LambdaExpression? predicate, - Type returnType, - bool returnDefault) - => TranslateSingleResultOperator( - source, - predicate, - returnType, - returnDefault - ? EnumerableMethods.SingleOrDefaultWithoutPredicate - : EnumerableMethods.SingleWithoutPredicate); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSkip(ShapedQueryExpression source, Expression count) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var newCount = TranslateExpression(count); - if (newCount == null) - { - return null; - } - - count = newCount; - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.Skip.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression, - count)); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSkipWhile(ShapedQueryExpression source, LambdaExpression predicate) - => null; - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateSum(ShapedQueryExpression source, LambdaExpression? selector, Type resultType) - => TranslateScalarAggregate(source, selector, nameof(Enumerable.Sum), resultType); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateTake(ShapedQueryExpression source, Expression count) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var newCount = TranslateExpression(count); - if (newCount == null) - { - return null; - } - - count = newCount; - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.Take.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression, - count)); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateTakeWhile(ShapedQueryExpression source, LambdaExpression predicate) - => null; - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateThenBy( - ShapedQueryExpression source, - LambdaExpression keySelector, - bool ascending) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var newKeySelector = TranslateLambdaExpression(source, keySelector); - if (newKeySelector == null) - { - return null; - } - - keySelector = newKeySelector; - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - (ascending ? EnumerableMethods.ThenBy : EnumerableMethods.ThenByDescending) - .MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type, keySelector.ReturnType), - kafkaQueryExpression.ServerQueryExpression, - keySelector)); - - return source; - } - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateUnion(ShapedQueryExpression source1, ShapedQueryExpression source2) - => TranslateSetOperation(EnumerableMethods.Union, source1, source2); - - /// - /// 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. - /// - protected override ShapedQueryExpression? TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - var newPredicate = TranslateLambdaExpression(source, predicate, preserveType: true); - if (newPredicate == null) - { - return null; - } - - predicate = newPredicate; - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.Where.MakeGenericMethod(kafkaQueryExpression.CurrentParameter.Type), - kafkaQueryExpression.ServerQueryExpression, - predicate)); - - return source; - } - - private Expression? TranslateExpression(Expression expression, bool preserveType = false) - { - var translation = _expressionTranslator.Translate(expression); - if (translation == null && _expressionTranslator.TranslationErrorDetails != null) - { - AddTranslationErrorDetails(_expressionTranslator.TranslationErrorDetails); - } - - if (expression != null - && translation != null - && preserveType - && expression.Type != translation.Type) - { - translation = expression.Type == typeof(bool) - ? Expression.Equal(translation, Expression.Constant(true, translation.Type)) - : Expression.Convert(translation, expression.Type); - } - - return translation; - } - - private LambdaExpression? TranslateLambdaExpression( - ShapedQueryExpression shapedQueryExpression, - LambdaExpression lambdaExpression, - bool preserveType = false) - { - var lambdaBody = TranslateExpression(RemapLambdaBody(shapedQueryExpression, lambdaExpression), preserveType); - - return lambdaBody != null - ? Expression.Lambda( - lambdaBody, - ((KafkaQueryExpression)shapedQueryExpression.QueryExpression).CurrentParameter) - : null; - } - - private Expression RemapLambdaBody(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression) - { - var lambdaBody = ReplacingExpressionVisitor.Replace( - lambdaExpression.Parameters.Single(), shapedQueryExpression.ShaperExpression, lambdaExpression.Body); - - return ExpandSharedTypeEntities((KafkaQueryExpression)shapedQueryExpression.QueryExpression, lambdaBody); - } - - private Expression ExpandSharedTypeEntities(KafkaQueryExpression queryExpression, Expression lambdaBody) - => _weakEntityExpandingExpressionVisitor.Expand(queryExpression, lambdaBody); - - private sealed class SharedTypeEntityExpandingExpressionVisitor : ExpressionVisitor - { - private readonly KafkaExpressionTranslatingExpressionVisitor _expressionTranslator; - - private KafkaQueryExpression _queryExpression; - - public SharedTypeEntityExpandingExpressionVisitor(KafkaExpressionTranslatingExpressionVisitor expressionTranslator) - { - _expressionTranslator = expressionTranslator; - _queryExpression = null!; - } - - public string? TranslationErrorDetails - => _expressionTranslator.TranslationErrorDetails; - - public Expression Expand(KafkaQueryExpression queryExpression, Expression lambdaBody) - { - _queryExpression = queryExpression; - - return Visit(lambdaBody); - } - - protected override Expression VisitMember(MemberExpression memberExpression) - { - var innerExpression = Visit(memberExpression.Expression); - - return TryExpand(innerExpression, MemberIdentity.Create(memberExpression.Member)) - ?? memberExpression.Update(innerExpression); - } - - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var navigationName)) - { - source = Visit(source); - - return TryExpand(source, MemberIdentity.Create(navigationName)) - ?? methodCallExpression.Update(null!, new[] { source, methodCallExpression.Arguments[1] }); - } - - return base.VisitMethodCall(methodCallExpression); - } - - protected override Expression VisitExtension(Expression extensionExpression) - => extensionExpression is StructuralTypeShaperExpression or ShapedQueryExpression or GroupByShaperExpression - ? extensionExpression - : base.VisitExtension(extensionExpression); - - private Expression? TryExpand(Expression? source, MemberIdentity member) - { - source = source.UnwrapTypeConversion(out var convertedType); - if (source is not StructuralTypeShaperExpression shaper) - { - return null; - } - - if (shaper.StructuralType is not IEntityType) - { - return null; - } - - var entityType = (IEntityType)shaper.StructuralType; - - if (convertedType != null) - { - entityType = entityType.GetRootType().GetDerivedTypesInclusive() - .FirstOrDefault(et => et.ClrType == convertedType); - - if (entityType == null) - { - return null; - } - } - - var navigation = member.MemberInfo != null - ? entityType.FindNavigation(member.MemberInfo) - : entityType.FindNavigation(member.Name!); - - if (navigation == null) - { - return null; - } - - var targetEntityType = navigation.TargetEntityType; - if (targetEntityType == null - || !targetEntityType.IsOwned()) - { - return null; - } - - var foreignKey = navigation.ForeignKey; - if (navigation.IsCollection) - { - var innerShapedQuery = CreateShapedQueryExpressionStatic(targetEntityType); - var innerQueryExpression = (KafkaQueryExpression)innerShapedQuery.QueryExpression; - - var makeNullable = foreignKey.PrincipalKey.Properties - .Concat(foreignKey.Properties) - .Select(p => p.ClrType) - .Any(t => t.IsNullableType()); - - var outerKey = shaper.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.Properties - : foreignKey.PrincipalKey.Properties, - makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.PrincipalKey.Properties - : foreignKey.Properties, - makeNullable); - - var keyComparison = ExpressionExtensions.CreateEqualsExpression(outerKey, innerKey); - - var predicate = makeNullable - ? Expression.AndAlso( - outerKey is NewArrayExpression newArrayExpression - ? newArrayExpression.Expressions - .Select( - e => - { - var left = (e as UnaryExpression)?.Operand ?? e; - - return Expression.NotEqual(left, Expression.Constant(null, left.Type)); - }) - .Aggregate((l, r) => Expression.AndAlso(l, r)) - : Expression.NotEqual(outerKey, Expression.Constant(null, outerKey.Type)), - keyComparison) - : keyComparison; - - var correlationPredicate = _expressionTranslator.Translate(predicate)!; - innerQueryExpression.UpdateServerQueryExpression( - Expression.Call( - EnumerableMethods.Where.MakeGenericMethod(innerQueryExpression.CurrentParameter.Type), - innerQueryExpression.ServerQueryExpression, - Expression.Lambda(correlationPredicate, innerQueryExpression.CurrentParameter))); - - return innerShapedQuery; - } - - var entityProjectionExpression = - shaper.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression - ? (EntityProjectionExpression)_queryExpression.GetProjection(projectionBindingExpression) - : (EntityProjectionExpression)shaper.ValueBufferExpression; - var innerShaper = entityProjectionExpression.BindNavigation(navigation); - if (innerShaper == null) - { - var innerShapedQuery = CreateShapedQueryExpressionStatic(targetEntityType); - var innerQueryExpression = (KafkaQueryExpression)innerShapedQuery.QueryExpression; - - var makeNullable = foreignKey.PrincipalKey.Properties - .Concat(foreignKey.Properties) - .Select(p => p.ClrType) - .Any(t => t.IsNullableType()); - - var outerKey = shaper.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.Properties - : foreignKey.PrincipalKey.Properties, - makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValuesExpression( - navigation.IsOnDependent - ? foreignKey.PrincipalKey.Properties - : foreignKey.Properties, - makeNullable); - - if (foreignKey.Properties.Count > 1) - { - outerKey = Expression.New(AnonymousObject.AnonymousObjectCtor, outerKey); - innerKey = Expression.New(AnonymousObject.AnonymousObjectCtor, innerKey); - } - - var outerKeySelector = Expression.Lambda(_expressionTranslator.Translate(outerKey)!, _queryExpression.CurrentParameter); - var innerKeySelector = Expression.Lambda( - _expressionTranslator.Translate(innerKey)!, innerQueryExpression.CurrentParameter); - (outerKeySelector, innerKeySelector) = AlignKeySelectorTypes(outerKeySelector, innerKeySelector); - innerShaper = _queryExpression.AddNavigationToWeakEntityType( - entityProjectionExpression, navigation, innerQueryExpression, outerKeySelector, innerKeySelector); - } - - return innerShaper; - } - } - - private ShapedQueryExpression TranslateTwoParameterSelector(ShapedQueryExpression source, LambdaExpression resultSelector) - { - var transparentIdentifierType = source.ShaperExpression.Type; - var transparentIdentifierParameter = Expression.Parameter(transparentIdentifierType); - - Expression original1 = resultSelector.Parameters[0]; - var replacement1 = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Outer"); - Expression original2 = resultSelector.Parameters[1]; - var replacement2 = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Inner"); - var newResultSelector = Expression.Lambda( - new ReplacingExpressionVisitor( - new[] { original1, original2 }, new[] { replacement1, replacement2 }) - .Visit(resultSelector.Body), - transparentIdentifierParameter); - - return TranslateSelect(source, newResultSelector); - } - - private static Expression AccessField( - Type transparentIdentifierType, - Expression targetExpression, - string fieldName) - => Expression.Field(targetExpression, transparentIdentifierType.GetTypeInfo().GetDeclaredField(fieldName)!); - - private ShapedQueryExpression? TranslateScalarAggregate( - ShapedQueryExpression source, - LambdaExpression? selector, - string methodName, - Type returnType) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - selector = selector == null - || selector.Body == selector.Parameters[0] - ? Expression.Lambda( - kafkaQueryExpression.GetProjection( - new ProjectionBindingExpression( - kafkaQueryExpression, new ProjectionMember(), returnType)), - kafkaQueryExpression.CurrentParameter) - : TranslateLambdaExpression(source, selector, preserveType: true); - - if (selector == null - || selector.Body is EntityProjectionExpression) - { - return null; - } - - var method = GetMethod(); - method = method.GetGenericArguments().Length == 2 - ? method.MakeGenericMethod(typeof(ValueBuffer), selector.ReturnType) - : method.MakeGenericMethod(typeof(ValueBuffer)); - - kafkaQueryExpression.UpdateServerQueryExpression( - Expression.Call(method, kafkaQueryExpression.ServerQueryExpression, selector)); - - return source.UpdateShaperExpression(Expression.Convert(kafkaQueryExpression.GetSingleScalarProjection(), returnType)); - - MethodInfo GetMethod() - => methodName switch - { - nameof(Enumerable.Average) => EnumerableMethods.GetAverageWithSelector(selector.ReturnType), - nameof(Enumerable.Max) => EnumerableMethods.GetMaxWithSelector(selector.ReturnType), - nameof(Enumerable.Min) => EnumerableMethods.GetMinWithSelector(selector.ReturnType), - nameof(Enumerable.Sum) => EnumerableMethods.GetSumWithSelector(selector.ReturnType), - _ => throw new InvalidOperationException(CoreStrings.UnknownEntity("Aggregate Operator")) - }; - } - - private ShapedQueryExpression? TranslateSingleResultOperator( - ShapedQueryExpression source, - LambdaExpression? predicate, - Type returnType, - MethodInfo method) - { - var kafkaQueryExpression = (KafkaQueryExpression)source.QueryExpression; - - if (predicate != null) - { - var newSource = TranslateWhere(source, predicate); - if (newSource == null) - { - return null; - } - - source = newSource; - } - - kafkaQueryExpression.ConvertToSingleResult(method); - - return source.ShaperExpression.Type != returnType - ? source.UpdateShaperExpression(Expression.Convert(source.ShaperExpression, returnType)) - : source; - } - - private static ShapedQueryExpression TranslateSetOperation( - MethodInfo setOperationMethodInfo, - ShapedQueryExpression source1, - ShapedQueryExpression source2) - { - var kafkaQueryExpression1 = (KafkaQueryExpression)source1.QueryExpression; - var kafkaQueryExpression2 = (KafkaQueryExpression)source2.QueryExpression; - - kafkaQueryExpression1.ApplySetOperation(setOperationMethodInfo, kafkaQueryExpression2); - - if (setOperationMethodInfo.Equals(EnumerableMethods.Except)) - { - return source1; - } - - var makeNullable = setOperationMethodInfo != EnumerableMethods.Intersect; - - return source1.UpdateShaperExpression( - MatchShaperNullabilityForSetOperation( - source1.ShaperExpression, source2.ShaperExpression, makeNullable)); - } - - private static Expression MatchShaperNullabilityForSetOperation(Expression shaper1, Expression shaper2, bool makeNullable) - { - switch (shaper1) - { - case StructuralTypeShaperExpression entityShaperExpression1 - when shaper2 is StructuralTypeShaperExpression entityShaperExpression2: - return entityShaperExpression1.IsNullable != entityShaperExpression2.IsNullable - ? entityShaperExpression1.MakeNullable(makeNullable) - : entityShaperExpression1; - - case NewExpression newExpression1 - when shaper2 is NewExpression newExpression2: - var newArguments = new Expression[newExpression1.Arguments.Count]; - for (var i = 0; i < newArguments.Length; i++) - { - newArguments[i] = MatchShaperNullabilityForSetOperation( - newExpression1.Arguments[i], newExpression2.Arguments[i], makeNullable); - } - - return newExpression1.Update(newArguments); - - case MemberInitExpression memberInitExpression1 - when shaper2 is MemberInitExpression memberInitExpression2: - var newExpression = (NewExpression)MatchShaperNullabilityForSetOperation( - memberInitExpression1.NewExpression, memberInitExpression2.NewExpression, makeNullable); - - var memberBindings = new MemberBinding[memberInitExpression1.Bindings.Count]; - for (var i = 0; i < memberBindings.Length; i++) - { - var memberAssignment = memberInitExpression1.Bindings[i] as MemberAssignment; - Check.DebugAssert(memberAssignment != null, "Only member assignment bindings are supported"); - - memberBindings[i] = memberAssignment.Update( - MatchShaperNullabilityForSetOperation( - memberAssignment.Expression, ((MemberAssignment)memberInitExpression2.Bindings[i]).Expression, - makeNullable)); - } - - return memberInitExpression1.Update(newExpression, memberBindings); - - default: - return shaper1; - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs deleted file mode 100644 index 8ae12099..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaQueryableMethodTranslatingExpressionVisitorFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory -{ - /// - /// 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. - /// - public KafkaQueryableMethodTranslatingExpressionVisitorFactory( - QueryableMethodTranslatingExpressionVisitorDependencies dependencies) - { - Dependencies = dependencies; - } - - /// - /// Dependencies for this service. - /// - protected virtual QueryableMethodTranslatingExpressionVisitorDependencies Dependencies { get; } - - /// - /// 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. - /// - public virtual QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) - => new KafkaQueryableMethodTranslatingExpressionVisitor(Dependencies, queryCompilationContext); -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs b/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs deleted file mode 100644 index b2a53832..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.QueryingEnumerable.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using MASES.EntityFrameworkCore.KNet.Internal; -using System.Collections; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public partial class KafkaShapedQueryCompilingExpressionVisitor -{ - private sealed class QueryingEnumerable : IAsyncEnumerable, IEnumerable, IQueryingEnumerable - { - private readonly QueryContext _queryContext; - private readonly IEnumerable _innerEnumerable; - private readonly Func _shaper; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _queryLogger; - private readonly bool _standAloneStateManager; - private readonly bool _threadSafetyChecksEnabled; - - public QueryingEnumerable( - QueryContext queryContext, - IEnumerable innerEnumerable, - Func shaper, - Type contextType, - bool standAloneStateManager, - bool threadSafetyChecksEnabled) - { - _queryContext = queryContext; - _innerEnumerable = innerEnumerable; - _shaper = shaper; - _contextType = contextType; - _queryLogger = queryContext.QueryLogger; - _standAloneStateManager = standAloneStateManager; - _threadSafetyChecksEnabled = threadSafetyChecksEnabled; - } - - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - => new Enumerator(this, cancellationToken); - - public IEnumerator GetEnumerator() - => new Enumerator(this); - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - - public string ToQueryString() - => KafkaStrings.NoQueryStrings; - - private sealed class Enumerator : IEnumerator, IAsyncEnumerator - { - private readonly QueryContext _queryContext; - private readonly IEnumerable _innerEnumerable; - private readonly Func _shaper; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _queryLogger; - private readonly bool _standAloneStateManager; - private readonly CancellationToken _cancellationToken; - private readonly IConcurrencyDetector? _concurrencyDetector; - private readonly IExceptionDetector _exceptionDetector; - - private IEnumerator? _enumerator; - - public Enumerator(QueryingEnumerable queryingEnumerable, CancellationToken cancellationToken = default) - { - _queryContext = queryingEnumerable._queryContext; - _innerEnumerable = queryingEnumerable._innerEnumerable; - _shaper = queryingEnumerable._shaper; - _contextType = queryingEnumerable._contextType; - _queryLogger = queryingEnumerable._queryLogger; - _standAloneStateManager = queryingEnumerable._standAloneStateManager; - _cancellationToken = cancellationToken; - _exceptionDetector = _queryContext.ExceptionDetector; - Current = default!; - - _concurrencyDetector = queryingEnumerable._threadSafetyChecksEnabled - ? _queryContext.ConcurrencyDetector - : null; - } - - public T Current { get; private set; } - - object IEnumerator.Current - => Current!; - - public bool MoveNext() - { - try - { - _concurrencyDetector?.EnterCriticalSection(); - - try - { - return MoveNextHelper(); - } - finally - { - _concurrencyDetector?.ExitCriticalSection(); - } - } - catch (Exception exception) - { - if (_exceptionDetector.IsCancellation(exception)) - { - _queryLogger.QueryCanceled(_contextType); - } - else - { - _queryLogger.QueryIterationFailed(_contextType, exception); - } - - throw; - } - } - - public ValueTask MoveNextAsync() - { - try - { - _concurrencyDetector?.EnterCriticalSection(); - - try - { - _cancellationToken.ThrowIfCancellationRequested(); - - return ValueTask.FromResult(MoveNextHelper()); - } - finally - { - _concurrencyDetector?.ExitCriticalSection(); - } - } - catch (Exception exception) - { - if (_exceptionDetector.IsCancellation(exception, _cancellationToken)) - { - _queryLogger.QueryCanceled(_contextType); - } - else - { - _queryLogger.QueryIterationFailed(_contextType, exception); - } - - throw; - } - } - - private bool MoveNextHelper() - { - if (_enumerator == null) - { - EntityFrameworkEventSource.Log.QueryExecuting(); - - _enumerator = _innerEnumerable.GetEnumerator(); - _queryContext.InitializeStateManager(_standAloneStateManager); - } - - var hasNext = _enumerator.MoveNext(); - - Current = hasNext - ? _shaper(_queryContext, _enumerator.Current) - : default!; - - return hasNext; - } - - public void Dispose() - { - _enumerator?.Dispose(); - _enumerator = null; - } - - public ValueTask DisposeAsync() - { - var enumerator = _enumerator; - _enumerator = null; - - return enumerator.DisposeAsyncIfAvailable(); - } - - public void Reset() - => throw new NotSupportedException(CoreStrings.EnumerableResetNotSupported); - } - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs deleted file mode 100644 index a86416a3..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs +++ /dev/null @@ -1,411 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using static System.Linq.Expressions.Expression; -using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions; - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -public partial class KafkaShapedQueryCompilingExpressionVisitor -{ - private sealed class ShaperExpressionProcessingExpressionVisitor : ExpressionVisitor - { - private static readonly MethodInfo IncludeReferenceMethodInfo - = typeof(ShaperExpressionProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(IncludeReference))!; - - private static readonly MethodInfo IncludeCollectionMethodInfo - = typeof(ShaperExpressionProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(IncludeCollection))!; - - private static readonly MethodInfo MaterializeCollectionMethodInfo - = typeof(ShaperExpressionProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(MaterializeCollection))!; - - private static readonly MethodInfo MaterializeSingleResultMethodInfo - = typeof(ShaperExpressionProcessingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(MaterializeSingleResult))!; - - private static readonly MethodInfo CollectionAccessorAddMethodInfo - = typeof(IClrCollectionAccessor).GetTypeInfo().GetDeclaredMethod(nameof(IClrCollectionAccessor.Add))!; - - private readonly KafkaShapedQueryCompilingExpressionVisitor _kafkaShapedQueryCompilingExpressionVisitor; - private readonly bool _tracking; - private ParameterExpression? _valueBufferParameter; - - private readonly Dictionary _mapping = new(); - private readonly List _variables = new(); - private readonly List _expressions = new(); - private readonly Dictionary> _materializationContextBindings = new(); - - public ShaperExpressionProcessingExpressionVisitor( - KafkaShapedQueryCompilingExpressionVisitor kafkaShapedQueryCompilingExpressionVisitor, - KafkaQueryExpression kafkaQueryExpression, - bool tracking) - { - _kafkaShapedQueryCompilingExpressionVisitor = kafkaShapedQueryCompilingExpressionVisitor; - _valueBufferParameter = kafkaQueryExpression.CurrentParameter; - _tracking = tracking; - } - - private ShaperExpressionProcessingExpressionVisitor( - KafkaShapedQueryCompilingExpressionVisitor kafkaShapedQueryCompilingExpressionVisitor, - bool tracking) - { - _kafkaShapedQueryCompilingExpressionVisitor = kafkaShapedQueryCompilingExpressionVisitor; - _tracking = tracking; - } - - public LambdaExpression ProcessShaper(Expression shaperExpression) - { - var result = Visit(shaperExpression); - _expressions.Add(result); - result = Block(_variables, _expressions); - - // If parameter is null then the projection is not really server correlated so we can just put anything. - _valueBufferParameter ??= Parameter(typeof(ValueBuffer)); - - return Lambda(result, QueryCompilationContext.QueryContextParameter, _valueBufferParameter); - } - - protected override Expression VisitExtension(Expression extensionExpression) - { - switch (extensionExpression) - { - case StructuralTypeShaperExpression shaper: - { - var key = shaper.ValueBufferExpression; - if (!_mapping.TryGetValue(key, out var variable)) - { - variable = Parameter(shaper.StructuralType.ClrType); - _variables.Add(variable); - var innerShaper = - _kafkaShapedQueryCompilingExpressionVisitor.InjectEntityMaterializers(shaper); - innerShaper = Visit(innerShaper); - _expressions.Add(Assign(variable, innerShaper)); - _mapping[key] = variable; - } - - return variable; - } - - case ProjectionBindingExpression projectionBindingExpression: - { - var key = projectionBindingExpression; - if (!_mapping.TryGetValue(key, out var variable)) - { - variable = Parameter(projectionBindingExpression.Type); - _variables.Add(variable); - var queryExpression = (KafkaQueryExpression)projectionBindingExpression.QueryExpression; - _valueBufferParameter ??= queryExpression.CurrentParameter; - - var projectionIndex = queryExpression.GetProjection(projectionBindingExpression).GetConstantValue(); - - // We don't need to pass property when reading at top-level - _expressions.Add( - Assign( - variable, queryExpression.CurrentParameter.CreateValueBufferReadValueExpression( - projectionBindingExpression.Type, projectionIndex, property: null))); - _mapping[key] = variable; - } - - return variable; - } - - case IncludeExpression includeExpression: - { - var entity = Visit(includeExpression.EntityExpression); - var entityClrType = includeExpression.EntityExpression.Type; - var includingClrType = includeExpression.Navigation.DeclaringEntityType.ClrType; - var inverseNavigation = includeExpression.Navigation.Inverse; - var relatedEntityClrType = includeExpression.Navigation.TargetEntityType.ClrType; - if (includingClrType != entityClrType - && includingClrType.IsAssignableFrom(entityClrType)) - { - includingClrType = entityClrType; - } - - if (includeExpression.Navigation.IsCollection) - { - var collectionResultShaperExpression = (CollectionResultShaperExpression)includeExpression.NavigationExpression; - var shaperLambda = new ShaperExpressionProcessingExpressionVisitor( - _kafkaShapedQueryCompilingExpressionVisitor, _tracking) - .ProcessShaper(collectionResultShaperExpression.InnerShaper); - _expressions.Add( - Call( - IncludeCollectionMethodInfo.MakeGenericMethod(entityClrType, includingClrType, relatedEntityClrType), - QueryCompilationContext.QueryContextParameter, - Visit(collectionResultShaperExpression.Projection), - Constant(shaperLambda.Compile()), - entity, - Constant(includeExpression.Navigation), - Constant(inverseNavigation, typeof(INavigationBase)), - Constant( - GenerateFixup( - includingClrType, relatedEntityClrType, includeExpression.Navigation, inverseNavigation) - .Compile()), - Constant(_tracking), -#pragma warning disable EF1001 // Internal EF Core API usage. - Constant(includeExpression.SetLoaded))); -#pragma warning restore EF1001 // Internal EF Core API usage. - } - else - { - _expressions.Add( - Call( - IncludeReferenceMethodInfo.MakeGenericMethod(entityClrType, includingClrType, relatedEntityClrType), - QueryCompilationContext.QueryContextParameter, - entity, - Visit(includeExpression.NavigationExpression), - Constant(includeExpression.Navigation), - Constant(inverseNavigation, typeof(INavigationBase)), - Constant( - GenerateFixup( - includingClrType, relatedEntityClrType, includeExpression.Navigation, inverseNavigation) - .Compile()), - Constant(_tracking))); - } - - return entity; - } - - case CollectionResultShaperExpression collectionResultShaperExpression: - { - var navigation = collectionResultShaperExpression.Navigation; - var collectionAccessor = navigation?.GetCollectionAccessor(); - var collectionType = collectionAccessor?.CollectionType ?? collectionResultShaperExpression.Type; - var elementType = collectionResultShaperExpression.ElementType; - var shaperLambda = new ShaperExpressionProcessingExpressionVisitor( - _kafkaShapedQueryCompilingExpressionVisitor, _tracking) - .ProcessShaper(collectionResultShaperExpression.InnerShaper); - - return Call( - MaterializeCollectionMethodInfo.MakeGenericMethod(elementType, collectionType), - QueryCompilationContext.QueryContextParameter, - Visit(collectionResultShaperExpression.Projection), - Constant(shaperLambda.Compile()), - Constant(collectionAccessor, typeof(IClrCollectionAccessor))); - } - - case SingleResultShaperExpression singleResultShaperExpression: - { - var shaperLambda = new ShaperExpressionProcessingExpressionVisitor( - _kafkaShapedQueryCompilingExpressionVisitor, _tracking) - .ProcessShaper(singleResultShaperExpression.InnerShaper); - - return Call( - MaterializeSingleResultMethodInfo.MakeGenericMethod(singleResultShaperExpression.Type), - QueryCompilationContext.QueryContextParameter, - Visit(singleResultShaperExpression.Projection), - Constant(shaperLambda.Compile())); - } - } - - return base.VisitExtension(extensionExpression); - } - - protected override Expression VisitBinary(BinaryExpression binaryExpression) - { - if (binaryExpression is { NodeType: ExpressionType.Assign, Left: ParameterExpression parameterExpression } - && parameterExpression.Type == typeof(MaterializationContext)) - { - var newExpression = (NewExpression)binaryExpression.Right; - - var projectionBindingExpression = (ProjectionBindingExpression)newExpression.Arguments[0]; - var queryExpression = (KafkaQueryExpression)projectionBindingExpression.QueryExpression; - _valueBufferParameter ??= queryExpression.CurrentParameter; - - _materializationContextBindings[parameterExpression] - = queryExpression.GetProjection(projectionBindingExpression).GetConstantValue>(); - - var updatedExpression = newExpression.Update( - new[] { Constant(ValueBuffer.Empty), newExpression.Arguments[1] }); - - return MakeBinary(ExpressionType.Assign, binaryExpression.Left, updatedExpression); - } - - if (binaryExpression is - { NodeType: ExpressionType.Assign, Left: MemberExpression { Member: FieldInfo { IsInitOnly: true } } memberExpression }) - { - return memberExpression.Assign(Visit(binaryExpression.Right)); - } - - return base.VisitBinary(binaryExpression); - } - - protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) - { - if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == ExpressionExtensions.ValueBufferTryReadValueMethod) - { - var property = methodCallExpression.Arguments[2].GetConstantValue(); - var indexMap = _materializationContextBindings[ - (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object!]; - - Check.DebugAssert( - property != null || methodCallExpression.Type.IsNullableType(), "Must read nullable value without property"); - - return Call( - methodCallExpression.Method, - _valueBufferParameter!, - Constant(indexMap[property!]), - methodCallExpression.Arguments[2]); - } - - return base.VisitMethodCall(methodCallExpression); - } - - private static void IncludeReference( - QueryContext queryContext, - TEntity entity, - TIncludedEntity? relatedEntity, - INavigationBase navigation, - INavigationBase? inverseNavigation, - Action fixup, - bool trackingQuery) - where TIncludingEntity : class, TEntity - where TEntity : class - where TIncludedEntity : class - { - if (entity is TIncludingEntity includingEntity) - { - if (trackingQuery - && navigation.DeclaringEntityType.FindPrimaryKey() != null) - { - // For non-null relatedEntity StateManager will set the flag - if (relatedEntity == null) - { - queryContext.SetNavigationIsLoaded(includingEntity, navigation); - } - } - else - { - navigation.SetIsLoadedWhenNoTracking(includingEntity); - if (relatedEntity != null) - { - fixup(includingEntity, relatedEntity); - if (inverseNavigation is { IsCollection: false }) - { - inverseNavigation.SetIsLoadedWhenNoTracking(relatedEntity); - } - } - } - } - } - - private static void IncludeCollection( - QueryContext queryContext, - IEnumerable innerValueBuffers, - Func innerShaper, - TEntity entity, - INavigationBase navigation, - INavigationBase? inverseNavigation, - Action fixup, - bool trackingQuery, - bool setLoaded) - where TIncludingEntity : class, TEntity - where TEntity : class - where TIncludedEntity : class - { - if (entity is TIncludingEntity includingEntity) - { - if (!navigation.IsShadowProperty()) - { - navigation.GetCollectionAccessor()!.GetOrCreate(includingEntity, forMaterialization: true); - } - - if (setLoaded) - { - if (trackingQuery) - { - queryContext.SetNavigationIsLoaded(entity, navigation); - } - else - { - navigation.SetIsLoadedWhenNoTracking(entity); - } - } - - foreach (var valueBuffer in innerValueBuffers) - { - var relatedEntity = innerShaper(queryContext, valueBuffer); - - if (!trackingQuery) - { - fixup(includingEntity, relatedEntity); - inverseNavigation?.SetIsLoadedWhenNoTracking(relatedEntity); - } - } - } - } - - private static TCollection MaterializeCollection( - QueryContext queryContext, - IEnumerable innerValueBuffers, - Func innerShaper, - IClrCollectionAccessor? clrCollectionAccessor) - where TCollection : class, ICollection - { - var collection = (TCollection)(clrCollectionAccessor?.Create() ?? new List()); - - foreach (var valueBuffer in innerValueBuffers) - { - var element = innerShaper(queryContext, valueBuffer); - collection.Add(element); - } - - return collection; - } - - private static TResult? MaterializeSingleResult( - QueryContext queryContext, - ValueBuffer valueBuffer, - Func innerShaper) - => valueBuffer.IsEmpty - ? default - : innerShaper(queryContext, valueBuffer); - - private static LambdaExpression GenerateFixup( - Type entityType, - Type relatedEntityType, - INavigationBase navigation, - INavigationBase? inverseNavigation) - { - var entityParameter = Parameter(entityType); - var relatedEntityParameter = Parameter(relatedEntityType); - var expressions = new List(); - - if (!navigation.IsShadowProperty()) - { - expressions.Add( - navigation.IsCollection - ? AddToCollectionNavigation(entityParameter, relatedEntityParameter, navigation) - : AssignReferenceNavigation(entityParameter, relatedEntityParameter, navigation)); - } - - if (inverseNavigation != null - && !inverseNavigation.IsShadowProperty()) - { - expressions.Add( - inverseNavigation.IsCollection - ? AddToCollectionNavigation(relatedEntityParameter, entityParameter, inverseNavigation) - : AssignReferenceNavigation(relatedEntityParameter, entityParameter, inverseNavigation)); - } - - return Lambda(Block(typeof(void), expressions), entityParameter, relatedEntityParameter); - } - - private static Expression AssignReferenceNavigation( - ParameterExpression entity, - ParameterExpression relatedEntity, - INavigationBase navigation) - => entity.MakeMemberAccess(navigation.GetMemberInfo(forMaterialization: true, forSet: true)).Assign(relatedEntity); - - private static Expression AddToCollectionNavigation( - ParameterExpression entity, - ParameterExpression relatedEntity, - INavigationBase navigation) - => Call( - Constant(navigation.GetCollectionAccessor()), - CollectionAccessorAddMethodInfo, - entity, - relatedEntity, - Constant(true)); - } -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.cs b/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.cs deleted file mode 100644 index 1470fe98..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryCompilingExpressionVisitor.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -using static Expression; - -public partial class KafkaShapedQueryCompilingExpressionVisitor : ShapedQueryCompilingExpressionVisitor -{ - private readonly Type _contextType; - private readonly bool _threadSafetyChecksEnabled; - - /// - /// 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. - /// - public KafkaShapedQueryCompilingExpressionVisitor( - ShapedQueryCompilingExpressionVisitorDependencies dependencies, - QueryCompilationContext queryCompilationContext) - : base(dependencies, queryCompilationContext) - { - _contextType = queryCompilationContext.ContextType; - _threadSafetyChecksEnabled = dependencies.CoreSingletonOptions.AreThreadSafetyChecksEnabled; - } - - /// - /// 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. - /// - protected override Expression VisitExtension(Expression extensionExpression) - { - switch (extensionExpression) - { - case KafkaTableExpression kafkaTableExpression: - return Call( - TableMethodInfo, - QueryCompilationContext.QueryContextParameter, - Constant(kafkaTableExpression.EntityType)); - } - - return base.VisitExtension(extensionExpression); - } - - /// - /// 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. - /// - protected override Expression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression) - { - var kafkaQueryExpression = (KafkaQueryExpression)shapedQueryExpression.QueryExpression; - kafkaQueryExpression.ApplyProjection(); - - var shaperExpression = new ShaperExpressionProcessingExpressionVisitor( - this, kafkaQueryExpression, QueryCompilationContext.QueryTrackingBehavior == QueryTrackingBehavior.TrackAll) - .ProcessShaper(shapedQueryExpression.ShaperExpression); - var innerEnumerable = Visit(kafkaQueryExpression.ServerQueryExpression); - - return New( - typeof(QueryingEnumerable<>).MakeGenericType(shaperExpression.ReturnType).GetConstructors()[0], - QueryCompilationContext.QueryContextParameter, - innerEnumerable, - Constant(shaperExpression.Compile()), - Constant(_contextType), - Constant( - QueryCompilationContext.QueryTrackingBehavior == QueryTrackingBehavior.NoTrackingWithIdentityResolution), - Constant(_threadSafetyChecksEnabled)); - } - - private static readonly MethodInfo TableMethodInfo - = typeof(KafkaShapedQueryCompilingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(Table))!; - - private static IEnumerable Table( - QueryContext queryContext, - IEntityType entityType) - => ((KafkaQueryContext)queryContext).GetValueBuffers(entityType); -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryExpressionVisitorFactory.cs b/src/net/KEFCore/Query/Internal8/KafkaShapedQueryExpressionVisitorFactory.cs deleted file mode 100644 index 1728a65d..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaShapedQueryExpressionVisitorFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory -{ - /// - /// 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. - /// - public KafkaShapedQueryCompilingExpressionVisitorFactory( - ShapedQueryCompilingExpressionVisitorDependencies dependencies) - { - Dependencies = dependencies; - } - - /// - /// Dependencies for this service. - /// - protected virtual ShapedQueryCompilingExpressionVisitorDependencies Dependencies { get; } - - /// - /// 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. - /// - public virtual ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext queryCompilationContext) - => new KafkaShapedQueryCompilingExpressionVisitor(Dependencies, queryCompilationContext); -} diff --git a/src/net/KEFCore/Query/Internal8/KafkaTableExpression.cs b/src/net/KEFCore/Query/Internal8/KafkaTableExpression.cs deleted file mode 100644 index 524ebeaf..00000000 --- a/src/net/KEFCore/Query/Internal8/KafkaTableExpression.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class KafkaTableExpression : Expression, IPrintableExpression -{ - /// - /// 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. - /// - public KafkaTableExpression(IEntityType entityType) - { - EntityType = entityType; - } - - /// - /// 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. - /// - public override Type Type - => typeof(IEnumerable); - - /// - /// 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. - /// - public virtual IEntityType EntityType { get; } - - /// - /// 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. - /// - public sealed override ExpressionType NodeType - => ExpressionType.Extension; - - /// - /// 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. - /// - protected override Expression VisitChildren(ExpressionVisitor visitor) - => this; - - /// - /// 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. - /// - void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) - => expressionPrinter.Append(nameof(KafkaTableExpression) + ": Entity: " + EntityType.DisplayName()); -} diff --git a/src/net/KEFCore/Query/Internal8/SingleResultShaperExpression.cs b/src/net/KEFCore/Query/Internal8/SingleResultShaperExpression.cs deleted file mode 100644 index db581cb1..00000000 --- a/src/net/KEFCore/Query/Internal8/SingleResultShaperExpression.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace MASES.EntityFrameworkCore.KNet.Query.Internal; - -/// -/// 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. -/// -public class SingleResultShaperExpression : Expression, IPrintableExpression -{ - /// - /// 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. - /// - public SingleResultShaperExpression( - Expression projection, - Expression innerShaper) - { - Projection = projection; - InnerShaper = innerShaper; - Type = innerShaper.Type; - } - - /// - /// 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. - /// - protected override Expression VisitChildren(ExpressionVisitor visitor) - { - var projection = visitor.Visit(Projection); - var innerShaper = visitor.Visit(InnerShaper); - - return Update(projection, innerShaper); - } - - /// - /// 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. - /// - public virtual SingleResultShaperExpression Update(Expression projection, Expression innerShaper) - => projection != Projection || innerShaper != InnerShaper - ? new SingleResultShaperExpression(projection, innerShaper) - : this; - - /// - /// 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. - /// - public sealed override ExpressionType NodeType - => ExpressionType.Extension; - - /// - /// 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. - /// - public override Type Type { get; } - - /// - /// 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. - /// - public virtual Expression Projection { get; } - - /// - /// 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. - /// - public virtual Expression InnerShaper { get; } - - /// - /// 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. - /// - void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) - { - expressionPrinter.AppendLine($"{nameof(SingleResultShaperExpression)}:"); - using (expressionPrinter.Indent()) - { - expressionPrinter.Append("("); - expressionPrinter.Visit(Projection); - expressionPrinter.Append(", "); - expressionPrinter.Visit(InnerShaper); - expressionPrinter.AppendLine(")"); - } - } -}