From a2a80db136146ff24965e9cfdba13fc018d72e01 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 14 Dec 2021 22:39:06 -0800 Subject: [PATCH 01/64] Compile for net6 with no warnings --- Directory.Build.props | 2 +- .../Cryptography/GenericHashingService.cs | 8 ++++---- .../Cryptography/MD5HashingService.cs | 9 ++++----- .../Cryptography/SHA1HashingService.cs | 8 ++++---- .../Cryptography/SHA256HashingService.cs | 8 ++++---- .../Cryptography/SHA384HashingService.cs | 8 ++++---- .../Cryptography/SHA512HashingService.cs | 8 ++++---- .../Sql.Drivers.Sqlite/ProviderInitializer.cs | 8 ++++---- .../Storage/Randomized/RandomizedTest.cs | 8 ++++---- .../Upgrade/ConflictsByTable/TestBase.cs | 10 ++-------- Orm/Xtensive.Orm/Orm/Providers/NameBuilder.cs | 4 ++-- Orm/Xtensive.Orm/Orm/QueryableExtensions.cs | 12 ++++++------ 12 files changed, 43 insertions(+), 50 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5424169cb9..9f6115910c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -40,7 +40,7 @@ true true - net5.0 + net6.0 9.0 $([MSBuild]::EnsureTrailingSlash( $([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildThisFileDirectory)', 'Orm.sln')))) diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/GenericHashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/GenericHashingService.cs index a725e6ac8e..7abe94ddb8 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/GenericHashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/GenericHashingService.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.06.10 @@ -34,7 +34,7 @@ public abstract class GenericHashingService : IHashingService protected byte[] GetSalt() { var salt = new byte[SaltSize]; - using (var rng = new RNGCryptoServiceProvider()) { + using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); return salt; } diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs index ba57eafb89..5e3902b2a5 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs @@ -1,11 +1,10 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.05.22 using System.Security.Cryptography; -using System.Text; using Xtensive.IoC; namespace Xtensive.Orm.Security.Cryptography @@ -19,7 +18,7 @@ public class MD5HashingService : GenericHashingService /// protected override HashAlgorithm GetHashAlgorithm() { - return new MD5CryptoServiceProvider(); + return MD5.Create(); } /// diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/SHA1HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/SHA1HashingService.cs index 03a71e6cc3..5410ab8e26 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/SHA1HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/SHA1HashingService.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.05.22 @@ -18,7 +18,7 @@ public class SHA1HashingService : GenericHashingService /// protected override HashAlgorithm GetHashAlgorithm() { - return new SHA1Managed(); + return SHA1.Create(); } /// diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/SHA256HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/SHA256HashingService.cs index 01ad385ccb..2b8096da55 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/SHA256HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/SHA256HashingService.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.05.22 @@ -18,7 +18,7 @@ public class SHA256HashingService : GenericHashingService /// protected override HashAlgorithm GetHashAlgorithm() { - return new SHA256Managed(); + return SHA256.Create(); } /// diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/SHA384HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/SHA384HashingService.cs index f2a0b4e99b..df08473451 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/SHA384HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/SHA384HashingService.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.05.22 @@ -18,7 +18,7 @@ public class SHA384HashingService : GenericHashingService /// protected override HashAlgorithm GetHashAlgorithm() { - return new SHA384Managed(); + return SHA384.Create(); } /// diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/SHA512HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/SHA512HashingService.cs index 817a6b1e54..fb98e2a6cc 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/SHA512HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/SHA512HashingService.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2011 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2011-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Dmitri Maximov // Created: 2011.05.22 @@ -18,7 +18,7 @@ public class SHA512HashingService : GenericHashingService /// protected override HashAlgorithm GetHashAlgorithm() { - return new SHA512Managed(); + return SHA512.Create(); } /// diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs index 0f25299790..3829d3ddb7 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2012 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2012-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov // Created: 2012.08.21 @@ -53,7 +53,7 @@ private static Stream GetLibraryStream() private static string GetLibraryHash() { - using (var hashProvider = new SHA1Managed()) { + using (var hashProvider = SHA1.Create()) { hashProvider.Initialize(); using (var stream = GetLibraryStream()) hashProvider.ComputeHash(stream); diff --git a/Orm/Xtensive.Orm.Tests/Storage/Randomized/RandomizedTest.cs b/Orm/Xtensive.Orm.Tests/Storage/Randomized/RandomizedTest.cs index eef2d71bbe..7533f3733e 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/Randomized/RandomizedTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/Randomized/RandomizedTest.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexander Nikolaev // Created: 2009.11.26 @@ -266,7 +266,7 @@ private void ThrowOrCompleteTransaction(TransactionScope tx) private static int GetSeed() { var bytes = new byte[sizeof (int)]; - var seedProvider = new RNGCryptoServiceProvider(); + using var seedProvider = RandomNumberGenerator.Create(); seedProvider.GetNonZeroBytes(bytes); return BitConverter.ToInt32(bytes, 0); } diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs b/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs index 2d253e3218..c1ffefe912 100644 --- a/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs +++ b/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs @@ -3,19 +3,13 @@ // See the License.txt file in the project root for more information. using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using NUnit.Framework; using Xtensive.Core; -using Xtensive.Modelling.Actions; using Xtensive.Orm.Configuration; using Xtensive.Orm.Model; using Xtensive.Orm.Upgrade; -using V1 = Xtensive.Orm.Tests.Upgrade.ConflictsByTable.ExactTableStructureNoGeneratorTestModel.Before; -using V2 = Xtensive.Orm.Tests.Upgrade.ConflictsByTable.ExactTableStructureNoGeneratorTestModel.After; -using TheTestHelper = Xtensive.Orm.Tests.Upgrade.ConflictsByTable.ExactTableStructureNoGeneratorTestModel.TestHelper; namespace Xtensive.Orm.Tests.Upgrade.ConflictsByTable { @@ -35,8 +29,8 @@ public async ValueTask PerformSafelyTest(InheritanceSchema inheritanceSchema, bo var upgradeDomainTypes = GetTypes(inheritanceSchema, true); var ex = (asyncBuild) - ? Assert.ThrowsAsync(async () => { using (await BuildDomainAsync(DomainUpgradeMode.PerformSafely, upgradeDomainTypes)) ; }) - : Assert.Throws(() => { using (BuildDomain(DomainUpgradeMode.PerformSafely, upgradeDomainTypes)) ; }); + ? Assert.ThrowsAsync(async () => { using (await BuildDomainAsync(DomainUpgradeMode.PerformSafely, upgradeDomainTypes)) {}}) + : Assert.Throws(() => { using (BuildDomain(DomainUpgradeMode.PerformSafely, upgradeDomainTypes)) {}}); CheckComparisonResult(inheritanceSchema, ex.ComparisonResult); diff --git a/Orm/Xtensive.Orm/Orm/Providers/NameBuilder.cs b/Orm/Xtensive.Orm/Orm/Providers/NameBuilder.cs index 915e334212..040ba9b276 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/NameBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/NameBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2020 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Dmitri Maximov @@ -577,7 +577,7 @@ public string ApplyNamingRules(string name) /// Computed hash. private static string GetHash(string name) { - using (var hashAlgorithm = new MD5CryptoServiceProvider()) { + using (var hashAlgorithm = MD5.Create()) { byte[] hash = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(name)); return $"H{hash[0]:x2}{hash[1]:x2}{hash[2]:x2}{hash[3]:x2}"; } diff --git a/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs b/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs index bb32cae5ec..08b7b5e169 100644 --- a/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs @@ -87,8 +87,8 @@ public static int Count([InstantHandle] this IQueryable source) } /// - /// Version of , where is specified as - /// . + /// Version of , where + /// is specified as . /// /// The type of the source element. /// The source sequence. @@ -135,8 +135,8 @@ public static IQueryable Skip(this IQueryable source, } /// - /// Version of , where is specified as - /// . + /// Version of , where + /// is specified as . /// /// The type of the source element. /// The source sequence. @@ -159,8 +159,8 @@ public static TSource ElementAt(this IQueryable source, Expres } /// - /// Version of , where is specified as - /// . + /// Version of , where + /// is specified as . /// /// The type of the source element. /// The source sequence. From 944926733011ed9b331b6eec3d8a5a232a8f0622 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 20:09:02 -0800 Subject: [PATCH 02/64] Remove not used code from tuple transform --- .../Tuples/Transform/CutInTransformTest.cs | 109 ----------------- .../Core/Extensions/ArrayExtensions.cs | 32 ++--- .../Tuples/Transform/CombineTransform.cs | 23 ++-- .../Tuples/Transform/CutInTransform.cs | 111 ------------------ .../Tuples/Transform/CutInTransform{T}.cs | 54 --------- .../Tuples/Transform/CutOutTransform.cs | 88 -------------- .../Transform/Interfaces/ITupleTransform.cs | 46 -------- .../Transform/Internals/MapTransformTuple.cs | 11 +- .../Transform/Internals/MapTransformTuple1.cs | 13 +- .../Transform/Internals/MapTransformTuple3.cs | 15 +-- .../Internals/ReadOnlyTransformTuple.cs | 49 ++++---- .../Tuples/Transform/MapTransform.cs | 32 +---- .../Tuples/Transform/ReadOnlyTransform.cs | 34 +++--- .../Tuples/Transform/SegmentTransform.cs | 21 ++-- .../Tuples/Transform/TransformedTuple.cs | 30 +---- .../TransformedTuple{TTupleTransform}.cs | 15 ++- .../Tuples/Transform/TupleTransformBase.cs | 51 ++++---- .../Transform/WrappingTransformTupleBase.cs | 89 -------------- 18 files changed, 122 insertions(+), 701 deletions(-) delete mode 100644 Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/CutInTransformTest.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/CutInTransform{T}.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/CutOutTransform.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Interfaces/ITupleTransform.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/WrappingTransformTupleBase.cs diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/CutInTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/CutInTransformTest.cs deleted file mode 100644 index 47c4971b95..0000000000 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/CutInTransformTest.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Elena Vakhtina -// Created: 2008.06.23 - -using System; -using NUnit.Framework; -using Xtensive.Comparison; -using Xtensive.Tuples; -using Xtensive.Orm.Tests; -using Xtensive.Tuples.Transform; -using Tuple = Xtensive.Tuples.Tuple; - - -namespace Xtensive.Orm.Tests.Core.Tuples.Transform -{ - [TestFixture] - public class CutInTransformTest - { - public const int IterationCount = 1000000; - public static Xtensive.Tuples.Tuple t1 = Xtensive.Tuples.Tuple.Create(3, 4.0, "5"); - public static Xtensive.Tuples.Tuple t2 = Xtensive.Tuples.Tuple.Create(1, "2"); - public const int CutInIndex = 1; - public const string value = "qwe"; - - [Test] - public void BaseTest() - { - TestLog.InfoRegion("CutInTransform test"); - TestLog.Info("Originals: {0}, {1}, index {2}", t1, t2, CutInIndex); - CutInTransform ct = new CutInTransform(false, CutInIndex, t1.Descriptor, t2.Descriptor); - CutInTransform ctro = new CutInTransform(true, CutInIndex, t1.Descriptor, t2.Descriptor); - Xtensive.Tuples.Tuple wt1 = ct.Apply(TupleTransformType.TransformedTuple, t1, t2); - TestLog.Info("Wrapper: {0}", wt1); - Xtensive.Tuples.Tuple ct1 = ct.Apply(TupleTransformType.Tuple, t1, t2); - TestLog.Info("Copy: {0}", ct1); - Xtensive.Tuples.Tuple wt2 = ctro.Apply(TupleTransformType.TransformedTuple, t1, t2); - Xtensive.Tuples.Tuple ct2 = ctro.Apply(TupleTransformType.Tuple, t1, t2); - Assert.AreEqual(wt1, wt2); - Assert.AreEqual(wt2, ct1); - Assert.AreEqual(ct1, ct2); - - TestLog.InfoRegion("CutInTransform test"); - TestLog.Info("Originals: {0}, {1}, index {2}", t1, value, CutInIndex); - CutInTransform ctt = new CutInTransform(false, CutInIndex, t1.Descriptor); - CutInTransform cttro = new CutInTransform(true, CutInIndex, t1.Descriptor); - Xtensive.Tuples.Tuple wtt1 = ctt.Apply(TupleTransformType.TransformedTuple, t1, value); - TestLog.Info("Wrapper: {0}", wtt1); - Xtensive.Tuples.Tuple ctt1 = ctt.Apply(TupleTransformType.Tuple, t1, value); - TestLog.Info("Copy: {0}", ctt1); - Xtensive.Tuples.Tuple wtt2 = cttro.Apply(TupleTransformType.TransformedTuple, t1, value); - Xtensive.Tuples.Tuple ctt2 = cttro.Apply(TupleTransformType.Tuple, t1, value); - Assert.AreEqual(wtt1, wtt2); - Assert.AreEqual(wtt2, ctt1); - Assert.AreEqual(ctt1, ctt2); - - } - - [Test] - [Explicit] - [Category("Performance")] - public void PerformanceTest() - { - TestLog.InfoRegion("CutInTransform test"); - CutInTransform mt = new CutInTransform(false, CutInIndex, t1.Descriptor, t1.Descriptor); - Xtensive.Tuples.Tuple wt1 = mt.Apply(TupleTransformType.TransformedTuple, t1, t1); - Xtensive.Tuples.Tuple wt2 = mt.Apply(TupleTransformType.TransformedTuple, t1, t1); - Xtensive.Tuples.Tuple ct1 = mt.Apply(TupleTransformType.Tuple, t1, t1); - Xtensive.Tuples.Tuple ct2 = mt.Apply(TupleTransformType.Tuple, t1, t1); - PerformanceTransformTesting(ct1, ct2, wt1, wt2); - - TestLog.InfoRegion("CutInTransform test"); - CutInTransform mtt = new CutInTransform(false, CutInIndex, t1.Descriptor); - Xtensive.Tuples.Tuple wtt1 = mtt.Apply(TupleTransformType.TransformedTuple, t1, value); - Xtensive.Tuples.Tuple wtt2 = mtt.Apply(TupleTransformType.TransformedTuple, t1, value); - Xtensive.Tuples.Tuple ctt1 = mtt.Apply(TupleTransformType.Tuple, t1, value); - Xtensive.Tuples.Tuple ctt2 = mtt.Apply(TupleTransformType.Tuple, t1, value); - PerformanceTransformTesting(ctt1, ctt2, wtt1, wtt2); - - } - - public void PerformanceTransformTesting(Xtensive.Tuples.Tuple tuple1, Xtensive.Tuples.Tuple tuple2, Xtensive.Tuples.Tuple tuple3, Xtensive.Tuples.Tuple tuple4) - { - int count = IterationCount; - - AdvancedComparerStruct comparer = AdvancedComparerStruct.Default; - comparer.Equals(tuple1, tuple2); - comparer.Equals(tuple1, tuple3); - comparer.Equals(tuple3, tuple4); - - TestHelper.CollectGarbage(); - using (new Measurement("O&O", MeasurementOptions.Log, count)) - for (int i = 0; i < count; i++) - comparer.Equals(tuple1, tuple2); - - TestHelper.CollectGarbage(); - using (new Measurement("O&W", MeasurementOptions.Log, count)) - for (int i = 0; i < count; i++) - comparer.Equals(tuple1, tuple3); - - TestHelper.CollectGarbage(); - using (new Measurement("W&W", MeasurementOptions.Log, count)) - for (int i = 0; i < count; i++) - comparer.Equals(tuple3, tuple4); - - } - } -} diff --git a/Orm/Xtensive.Orm/Core/Extensions/ArrayExtensions.cs b/Orm/Xtensive.Orm/Core/Extensions/ArrayExtensions.cs index 82dc1f40a0..a4b79caec7 100644 --- a/Orm/Xtensive.Orm/Core/Extensions/ArrayExtensions.cs +++ b/Orm/Xtensive.Orm/Core/Extensions/ArrayExtensions.cs @@ -60,22 +60,22 @@ public static void Copy(this TItem[] source, TItem[] target, int targetIn // target[targetIndex++] = source[i]; } - /// - /// Clones array with type case. - /// - /// The type of source array items. - /// The type of result array items. - /// Collection to convert. - /// An array containing all the items from the . - public static TNewItem[] Cast(this TItem[] source) - where TNewItem: TItem - { - var items = new TNewItem[source.Length]; - int i = 0; - foreach (TItem item in source) - items[i++] = (TNewItem)item; - return items; - } + // /// + // /// Clones array with type case. + // /// + // /// The type of source array items. + // /// The type of result array items. + // /// Collection to convert. + // /// An array containing all the items from the . + // public static TNewItem[] Cast(this TItem[] source) + // where TNewItem: TItem + // { + // var items = new TNewItem[source.Length]; + // int i = 0; + // foreach (TItem item in source) + // items[i++] = (TNewItem)item; + // return items; + // } /// /// Clones array with element conversion. diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs index e1a9f3096f..25cf2aed42 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs @@ -55,17 +55,11 @@ public override string ToString() // Constructors - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Source tuple descriptors. - public CombineTransform(bool isReadOnly, params TupleDescriptor[] sources) - : base(isReadOnly) + private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor[] sources, out Pair[] map) { int totalLength = sources.Sum(s => s.Count); var types = new Type[totalLength]; - var map = new Pair[totalLength]; + map = new Pair[totalLength]; int index = 0; for (int i = 0; i(i, j); } } + return TupleDescriptor.Create(types); + } + + /// + /// Initializes a new instance of this type. + /// + /// property value. + /// Source tuple descriptors. + public CombineTransform(bool isReadOnly, params TupleDescriptor[] sources) + : base(isReadOnly, CreateDescriptorAndMap(sources, out var map), map) + { this.sources = sources; - Descriptor = TupleDescriptor.Create(types); - SetMap(map); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs deleted file mode 100644 index c69285f892..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) a Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Elena Vakhtina -// Created: 20.06.2008 - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Xtensive.Core; -using Xtensive.Reflection; - -namespace Xtensive.Tuples.Transform -{ - /// - /// Cuts in specified value to the . - /// - public class CutInTransform : MapTransform - { - private int index; - private TupleDescriptor[] sources; - - /// - /// Gets the start index at witch this transform cuts in specified value. - /// - public int Index - { - [DebuggerStepThrough] - get { return index; } - } - - /// - /// Gets tuple descriptors this transform cuts in. - /// - public IReadOnlyList Sources - { - [DebuggerStepThrough] - get => Array.AsReadOnly(sources); - } - - /// - public new Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) - { - return base.Apply(transformType, source1, source2); - } - - /// - public override string ToString() - { - string description = $"{sources.ToDelimitedString(" + ")}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), - description); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Start index. - /// Source tuple descriptors. - public CutInTransform(bool isReadOnly, int index , params TupleDescriptor[] sources) - : base(isReadOnly) - { - this.index = index; - int totalLength = sources.Sum(s => s.Count); - Type[] types = new Type[totalLength]; - Pair[] map = new Pair[totalLength]; - TupleDescriptor sourceDescriptor = sources[0]; - TupleDescriptor cutInDescriptor = sources[1]; - int sourceCount = sourceDescriptor.Count; - int cutInCount = cutInDescriptor.Count; - - bool isIndex = false; - bool isEndOfTuple = false; - int ind = 0; - - if (index == sourceCount) { - sourceCount++; - isEndOfTuple = true; - } - else if (index < 0 || index > sourceCount) - throw new ArgumentOutOfRangeException("index"); - for (int i = 0; i < sourceCount; i++) - { - if ((i == index) && !isIndex) { - for (int j = 0; j < cutInCount; j++) - { - types[ind] = cutInDescriptor[j]; - map[ind++] = new Pair(1, j); - } - if (!isEndOfTuple) { - i--; - isIndex = true; - } - } - else { - types[ind] = sourceDescriptor[i]; - map[ind++] = new Pair(0, i); - } - } - this.sources = sources; - Descriptor = TupleDescriptor.Create(types); - SetMap(map); - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform{T}.cs b/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform{T}.cs deleted file mode 100644 index b8aaad88c5..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform{T}.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) a Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Elena Vakhtina -// Created: 20.06.2008 - -using System; -using System.Collections.Generic; -using System.Linq; -using Xtensive.Collections; -using Xtensive.Reflection; -using Xtensive.Tuples.Transform; -using Xtensive.Tuples.Transform.Internals; - - - -namespace Xtensive.Tuples.Transform -{ - /// - /// Cuts in specified value to the . - /// - public sealed class CutInTransform : CutInTransform - { - - /// - public Tuple Apply(TupleTransformType transformType, Tuple source1, T source2) - { - return Apply(transformType, source1, Tuple.Create(source2)); - } - - /// - public override string ToString() - { - string description = $"Index {Index}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), - description); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Start index. - /// Source tuple descriptor. - public CutInTransform(bool isReadOnly, int index, TupleDescriptor source1) - : base(isReadOnly, index, source1, TupleDescriptor.Create(new Type[]{typeof(T)})) - { - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CutOutTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CutOutTransform.cs deleted file mode 100644 index ab3a584a96..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CutOutTransform.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Elena Vakhtina -// Created: 2008.07.07 - -using System; -using System.Diagnostics; -using Xtensive.Core; -using Xtensive.Reflection; - - - - -namespace Xtensive.Tuples.Transform -{ - /// - /// Cuts out specified from the . - /// - public sealed class CutOutTransform : MapTransform - { - private Segment segment; - - /// - /// Gets the segment this transform cuts out. - /// - public Segment Segment - { - [DebuggerStepThrough] - get { return segment; } - } - - /// - public new Tuple Apply(TupleTransformType transformType, Tuple source) - { - return base.Apply(transformType, source); - } - - /// - public override string ToString() - { - string description = $"{segment}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), - description); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Source tuple descriptor. - /// The segment to cut out. - public CutOutTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) - : base(isReadOnly) - { - this.segment = segment; - Type[] fields = new Type[sourceDescriptor.Count - segment.Length]; - int[] map = new int[sourceDescriptor.Count - segment.Length]; - int j = segment.Offset; - bool flag = false; - if (sourceDescriptor.Count >= j + segment.Length) - for (int i = 0; i < sourceDescriptor.Count - segment.Length; i++) { - if ((i < j)) { - fields[i] = sourceDescriptor[i]; - map[i] = i; - } - if (i == j) { - flag = true; - j += segment.Length; - } - if (flag) - { - fields[i] = sourceDescriptor[j]; - map[i] = j; - j++; - } - } - else - throw new InvalidOperationException(Strings.ExSegmentIsOutOfRange); - Descriptor = TupleDescriptor.Create(fields); - SetSingleSourceMap(map); - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Interfaces/ITupleTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/Interfaces/ITupleTransform.cs deleted file mode 100644 index 8036eaef5a..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Interfaces/ITupleTransform.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.04.30 - -using Xtensive.Tuples.Transform; - -namespace Xtensive.Tuples -{ - /// - /// Tuple transformation definition. - /// - public interface ITupleTransform - { - /// - /// Gets describing the tuples - /// this transform may produce. - /// means "any" (i.e. transform definition - /// is not descriptor-dependent). - /// - TupleDescriptor Descriptor { get; } - - /// - /// Gets the default result tuple. - /// Can be used to get default values for the result tuple fields. - /// Must be a read-only tuple. - /// - Tuple DefaultResult { get; } - - /// - /// Indicates whether transform always produces read-only tuples or not. - /// - bool IsReadOnly { get; } - - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// Transformation arguments. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - Tuple Apply(TupleTransformType transformType, params object[] arguments); - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index 19628222ce..91440428d6 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -20,13 +20,6 @@ public sealed class MapTransformTuple : TransformedTuple { private readonly Tuple[] tuples; - /// - public override IReadOnlyList Arguments - { - [DebuggerStepThrough] - get => Array.AsReadOnly(tuples); - } - #region GetFieldState, GetValue, SetValue methods /// @@ -52,7 +45,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (Transform.IsReadOnly) + if (TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); var indexes = TypedTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); @@ -62,7 +55,7 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && Transform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); var map = TypedTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index 7e2ca9dd2b..ba760e3bd4 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -19,15 +19,6 @@ public sealed class MapTransformTuple1 : TransformedTuple { private Tuple tuple; - /// - public override IReadOnlyList Arguments { - get { - Tuple[] copy = new Tuple[1]; - copy[0] = tuple; - return copy; - } - } - #region GetFieldState, GetValue, SetValue methods /// @@ -57,7 +48,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (Transform.IsReadOnly) + if (TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); tuple.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); } @@ -72,7 +63,7 @@ private int GetMappedFieldIndex(int fieldIndex) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && Transform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); var index = GetMappedFieldIndex(fieldIndex); return index == MapTransform.NoMapping diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs index fae607272e..4bc84b710e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs @@ -5,7 +5,6 @@ // Created: 2008.06.04 using System; -using System.Collections.Generic; using Xtensive.Collections; using Xtensive.Core; @@ -20,16 +19,6 @@ public sealed class MapTransformTuple3 : TransformedTuple { private FixedList3 tuples; - /// - public override IReadOnlyList Arguments { - get { - Tuple[] copy = new Tuple[tuples.Count]; - for (int i = 0; i < tuples.Count; i++) - copy[i] = tuples[i]; - return copy; - } - } - #region GetFieldState, GetValue, SetValue methods /// @@ -55,7 +44,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (Transform.IsReadOnly) + if (TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); Pair indexes = TypedTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); @@ -65,7 +54,7 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && Transform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) throw Exceptions.ObjectIsReadOnly(null); var map = TypedTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs index 3cca08abf2..af4443c428 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs @@ -5,8 +5,6 @@ // Created: 2007.06.15 using System; -using System.Collections.Generic; -using Xtensive.Collections; using Xtensive.Core; @@ -16,44 +14,40 @@ namespace Xtensive.Tuples.Transform.Internals /// A tuple wrapper for . /// [Serializable] - public sealed class ReadOnlyTransformTuple : WrappingTransformTupleBase + public sealed class ReadOnlyTransformTuple : TransformedTuple { + private readonly Tuple origin; + /// - public override ITupleTransform Transform { - get { - return ReadOnlyTransform.Instance; - } - } + public override TupleDescriptor Descriptor => origin.Descriptor; + + /// + public override int Count => origin.Count; /// - /// - /// This method always returns an empty array of s - /// to block any access to the original tuple. - /// - public override IReadOnlyList Arguments { - get { - return Array.Empty(); - } - } + public override TupleFieldState GetFieldState(int fieldIndex) => origin.GetFieldState(fieldIndex); - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - throw Exceptions.ObjectIsReadOnly(null); - } + /// + public override object GetValue(int fieldIndex, out TupleFieldState fieldState) => origin.GetValue(fieldIndex, out fieldState); + + protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) => Exceptions.ObjectIsReadOnly(null); /// - public override void SetValue(int fieldIndex, object fieldValue) - { - throw Exceptions.ObjectIsReadOnly(null); - } + public override void SetValue(int fieldIndex, object fieldValue) => throw Exceptions.ObjectIsReadOnly(null); protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && Transform.IsReadOnly) + if (isWriting) throw Exceptions.ObjectIsReadOnly(null); return origin.GetMappedContainer(fieldIndex, isWriting); } + /// + public override bool Equals(Tuple other) => origin.Equals(other); + + /// + public override int GetHashCode() => origin.GetHashCode(); + // Constructors @@ -62,8 +56,9 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// /// Tuple to provide read-only wrapper for. public ReadOnlyTransformTuple(Tuple tuple) - : base(tuple) { + ArgumentValidator.EnsureArgumentNotNull(tuple, nameof(tuple)); + origin = tuple; } } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index affb658db0..aa056e632d 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -102,22 +102,6 @@ protected void SetMap(Pair[] map) sourceCount = newSourceCount; } - /// - public override Tuple Apply(TupleTransformType transformType, params object[] arguments) - { - ArgumentValidator.EnsureArgumentNotNull(arguments, "arguments"); - switch (sourceCount) { - case 1: - return Apply(transformType, (Tuple)arguments[0]); - case 2: - return Apply(transformType, (Tuple)arguments[0], (Tuple)arguments[1]); - case 3: - return Apply(transformType, (Tuple)arguments[0], (Tuple)arguments[1], (Tuple)arguments[2]); - default: - return Apply(transformType, arguments.Cast()); - } - } - /// /// Applies the transformation. /// @@ -294,20 +278,10 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList property value. /// Initial property value. protected MapTransform(bool isReadOnly, TupleDescriptor descriptor) - : this(isReadOnly) + : base(descriptor) { - ArgumentValidator.EnsureArgumentNotNull(descriptor, "descriptor"); - Descriptor = descriptor; + ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); this.isReadOnly = isReadOnly; - } - - /// - /// Initializes a new instance of this type. - /// - /// property value. - protected MapTransform(bool isReadOnly) - { - this.isReadOnly = isReadOnly; - } + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs index b235e8d8b7..6f98ea58c7 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs @@ -40,15 +40,8 @@ public override bool IsReadOnly { } } - /// - public override Tuple Apply(TupleTransformType transformType, params object[] arguments) - { - ArgumentValidator.EnsureArgumentNotNull(arguments, "arguments"); - return Apply(transformType, arguments[0]); - } - /// - /// Typed version of . + /// Applies the transformation. /// /// The type of transformation to perform. /// Transformation argument. @@ -58,24 +51,25 @@ public override Tuple Apply(TupleTransformType transformType, params object[] ar public Tuple Apply(TupleTransformType transformType, Tuple source) { switch (transformType) { - case TupleTransformType.Auto: + case TupleTransformType.Auto: // TODO: Implement "Auto" for generated read-only tuples, when they'll be ready - case TupleTransformType.TransformedTuple: - if (source is ReadOnlyTransformTuple) - return source; - return new ReadOnlyTransformTuple(source); - case TupleTransformType.Tuple: - // TODO: Return generated read-only tuple copy - return new ReadOnlyTransformTuple(source.ToRegular()); - default: - throw new ArgumentOutOfRangeException("transformType"); + case TupleTransformType.TransformedTuple: + if (source is ReadOnlyTransformTuple) + return source; + return new ReadOnlyTransformTuple(source); + case TupleTransformType.Tuple: + // TODO: Return generated read-only tuple copy + return new ReadOnlyTransformTuple(source.ToRegular()); + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); } } - + // Constructors - + private ReadOnlyTransform() + : base(null) { } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 189385587e..7eae54749e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -47,6 +47,17 @@ public override string ToString() // Constructors + private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDescriptor, in Segment segment, out int[] map) + { + var fields = new Type[segment.Length]; + map = new int[segment.Length]; + for (int i = 0, j = segment.Offset; i < segment.Length; i++, j++) { + fields[i] = sourceDescriptor[j]; + map[i] = j; + } + return TupleDescriptor.Create(fields); + } + /// /// Initializes a new instance of this type. /// @@ -54,17 +65,9 @@ public override string ToString() /// Source tuple descriptor. /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) - : base(isReadOnly) + : base(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map) { this.segment = segment; - Type[] fields = new Type[segment.Length]; - int[] map = new int[segment.Length]; - for (int i = 0, j = segment.Offset; i < segment.Length; i++, j++) { - fields[i] = sourceDescriptor[j]; - map[i] = j; - } - Descriptor = TupleDescriptor.Create(fields); - SetSingleSourceMap(map); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs index 34a88c5e7d..faebb970c1 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs @@ -18,32 +18,12 @@ namespace Xtensive.Tuples.Transform [Serializable] public abstract class TransformedTuple : Tuple { - /// - /// Gets the transform used to produce this instance. - /// - public abstract ITupleTransform Transform { get; } - - /// - /// Gets a list of arguments used in method - /// to produce this tuple. - /// means arguments are unknown an this stage. - /// - public abstract IReadOnlyList Arguments { get; } - - /// - public override TupleDescriptor Descriptor - { - [DebuggerStepThrough] - get { return Transform.Descriptor; } - } + // /// + // /// Gets the transform used to produce this instance. + // /// + // public abstract bool IsReadOnly { get; } /// - public override string ToString() - { - return string.Format(Strings.TransformedTupleFormat, - base.ToString(), - Transform, - Arguments.ToCommaDelimitedString()); - } + public abstract override TupleDescriptor Descriptor { get; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs index 54b7567ab1..49a655e1cf 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs @@ -15,16 +15,12 @@ namespace Xtensive.Tuples.Transform /// [Serializable] public abstract class TransformedTuple : TransformedTuple - where TTupleTransform : ITupleTransform + where TTupleTransform : TupleTransformBase { private TTupleTransform transform; /// - public override ITupleTransform Transform - { - [DebuggerStepThrough] - get { return transform; } - } + public override TupleDescriptor Descriptor => transform.Descriptor; /// /// Gets or sets the transform used to produce this instance. @@ -36,6 +32,13 @@ protected set { } } + /// + public override string ToString() + { + return string.Format(Strings.TransformedTupleFormat, + base.ToString(), + transform, string.Empty); + } // Constructors diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs index 945eaa8fe9..c1ea3b441f 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs @@ -5,7 +5,6 @@ // Created: 2008.04.30 using System; -using System.Diagnostics; using Xtensive.Reflection; namespace Xtensive.Tuples.Transform @@ -14,43 +13,37 @@ namespace Xtensive.Tuples.Transform /// Base class for any tuple transform. /// [Serializable] - public abstract class TupleTransformBase : ITupleTransform + public abstract class TupleTransformBase { - private TupleDescriptor descriptor; private Tuple defaultResult; - /// - public TupleDescriptor Descriptor - { - get { return descriptor; } - protected set { - descriptor = value; - defaultResult = null; - } - } + /// + /// Gets describing the tuples + /// this transform may produce. + /// means "any" (i.e. transform definition + /// is not descriptor-dependent). + /// + public TupleDescriptor Descriptor { get; } - /// - public Tuple DefaultResult { - get { - if (defaultResult==null && descriptor!=null) - defaultResult = Tuple.Create(descriptor).ToReadOnly(TupleTransformType.Tuple); - return defaultResult; - } - } + /// + /// Gets the default result tuple. + /// Can be used to get default values for the result tuple fields. + /// Must be a read-only tuple. + /// + public Tuple DefaultResult => + Descriptor == null ? null : defaultResult ??= Tuple.Create(Descriptor).ToReadOnly(TupleTransformType.Tuple); - /// - public virtual bool IsReadOnly { - [DebuggerStepThrough] - get { return false; } - } + /// + /// Indicates whether transform always produces read-only tuples or not. + /// + public virtual bool IsReadOnly => false; /// - public abstract Tuple Apply(TupleTransformType transformType, params object[] arguments); + public override string ToString() => GetType().GetShortName(); - /// - public override string ToString() + protected TupleTransformBase(TupleDescriptor descriptor) { - return GetType().GetShortName(); + Descriptor = descriptor; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/WrappingTransformTupleBase.cs b/Orm/Xtensive.Orm/Tuples/Transform/WrappingTransformTupleBase.cs deleted file mode 100644 index 5d2c9aba60..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/WrappingTransformTupleBase.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Yakunin -// Created: 2008.06.02 - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for one-to-one tuple transformations. - /// - [Serializable] - public abstract class WrappingTransformTupleBase: TransformedTuple - { - protected readonly Tuple origin; - - /// - public override TupleDescriptor Descriptor - { - [DebuggerStepThrough] - get => origin.Descriptor; - } - - /// - public override int Count { - [DebuggerStepThrough] - get => origin.Count; - } - - /// - public override IReadOnlyList Arguments { - [DebuggerStepThrough] - get => new object[] {origin}; - } - - #region GetFieldState, GetValueOrDefault, SetValue methods - - /// - public override TupleFieldState GetFieldState(int fieldIndex) - { - return origin.GetFieldState(fieldIndex); - } - - /// - public override object GetValue(int fieldIndex, out TupleFieldState fieldState) - { - return origin.GetValue(fieldIndex, out fieldState); - } - - /// - public override void SetValue(int fieldIndex, object fieldValue) - { - origin.SetValue(fieldIndex, fieldValue); - } - - #endregion - - /// - public sealed override bool Equals(Tuple other) - { - return origin.Equals(other); - } - - /// - public sealed override int GetHashCode() - { - return origin.GetHashCode(); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Tuple to provide the wrapper for. - protected WrappingTransformTupleBase(Tuple tuple) - { - ArgumentValidator.EnsureArgumentNotNull(tuple, "tuple"); - origin = tuple; - } - } -} \ No newline at end of file From e437d1757c408a2c700df628463e36e9eec17ea8 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 20:47:48 -0800 Subject: [PATCH 03/64] Get rid of ReadOnlyTransform as it is not used --- .../Tuples/Transform/ReadOnlyTransformTest.cs | 81 ------------------- .../Tuples/Transform/CombineTransform.cs | 2 +- .../Transform/Internals/MapTransformTuple1.cs | 15 +++- .../Internals/ReadOnlyTransformTuple.cs | 64 --------------- .../Tuples/Transform/MapTransform.cs | 19 ++--- .../Tuples/Transform/ReadOnlyTransform.cs | 76 ----------------- .../Tuples/Transform/SegmentTransform.cs | 2 +- .../Tuples/Transform/TupleTransformBase.cs | 21 ++--- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 13 --- 9 files changed, 24 insertions(+), 269 deletions(-) delete mode 100644 Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/ReadOnlyTransformTest.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/ReadOnlyTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/ReadOnlyTransformTest.cs deleted file mode 100644 index 83be58a70b..0000000000 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/ReadOnlyTransformTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Yakunin -// Created: 2008.06.05 - -using System; -using NUnit.Framework; -using Xtensive.Comparison; -using Xtensive.Tuples; -using Xtensive.Orm.Tests; -using Xtensive.Tuples.Transform; -using Tuple = Xtensive.Tuples.Tuple; - -namespace Xtensive.Orm.Tests.Core.Tuples.Transform -{ - [TestFixture] - public class ReadOnlyTransformTest - { - public const int IterationCount = 1000000; - - [Test] - public void BaseTest() - { - Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1, "2", 3); - TestLog.Info("Original: {0}", t); - - Xtensive.Tuples.Tuple rt = t.ToReadOnly(TupleTransformType.TransformedTuple); - TestLog.Info("Wrapper: {0}", rt); - Assert.AreEqual(t, rt); - t.SetValue(0, 2); - Assert.AreEqual(t, rt); - t.SetValue(0, 1); - AssertEx.Throws(delegate { - rt.SetValue(0, 2); - }); - - Xtensive.Tuples.Tuple ct = t.ToReadOnly(TupleTransformType.Tuple); - TestLog.Info("Copy: {0}", ct); - Assert.AreEqual(t, ct); - t.SetValue(0, 2); - Assert.AreNotEqual(t, ct); - t.SetValue(0, 1); - AssertEx.Throws(delegate { - ct.SetValue(0, 2); - }); - } - - [Test] - [Explicit] - [Category("Performance")] - public void PerformanceTest() - { - AdvancedComparerStruct comparer = AdvancedComparerStruct.Default; - Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1, 2); - Xtensive.Tuples.Tuple ct = t.ToRegular(); - Xtensive.Tuples.Tuple wt = t.ToReadOnly(TupleTransformType.TransformedTuple); - Xtensive.Tuples.Tuple wtc = t.ToReadOnly(TupleTransformType.TransformedTuple); - int count = IterationCount; - - comparer.Equals(t, ct); - comparer.Equals(t, wt); - comparer.Equals(wt, wtc); - - TestHelper.CollectGarbage(); - using (new Measurement("O&O", MeasurementOptions.Log, count)) - for (int i = 0; i /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Source tuple descriptors. public CombineTransform(bool isReadOnly, params TupleDescriptor[] sources) : base(isReadOnly, CreateDescriptorAndMap(sources, out var map), map) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index ba760e3bd4..370e2fe727 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -5,7 +5,6 @@ // Created: 2008.06.04 using System; -using System.Collections.Generic; using Xtensive.Core; @@ -19,6 +18,14 @@ public sealed class MapTransformTuple1 : TransformedTuple { private Tuple tuple; + private Tuple defaultResult; + /// + /// Gets the default result tuple. + /// Can be used to get default values for the result tuple fields. + /// Must be a read-only tuple. + /// + private Tuple DefaultResult => defaultResult ??= Tuple.Create(TypedTransform.Descriptor); + #region GetFieldState, GetValue, SetValue methods /// @@ -40,9 +47,9 @@ protected internal override void SetFieldState(int fieldIndex, TupleFieldState f public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { int index = GetMappedFieldIndex(fieldIndex); - if (index==MapTransform.NoMapping) - return TypedTransform.DefaultResult.GetValue(fieldIndex, out fieldState); - return tuple.GetValue(index, out fieldState); + return index == MapTransform.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : tuple.GetValue(index, out fieldState); } /// diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs deleted file mode 100644 index af4443c428..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Nick Svetlov -// Created: 2007.06.15 - -using System; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform.Internals -{ - /// - /// A tuple wrapper for . - /// - [Serializable] - public sealed class ReadOnlyTransformTuple : TransformedTuple - { - private readonly Tuple origin; - - /// - public override TupleDescriptor Descriptor => origin.Descriptor; - - /// - public override int Count => origin.Count; - - /// - public override TupleFieldState GetFieldState(int fieldIndex) => origin.GetFieldState(fieldIndex); - - /// - public override object GetValue(int fieldIndex, out TupleFieldState fieldState) => origin.GetValue(fieldIndex, out fieldState); - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) => Exceptions.ObjectIsReadOnly(null); - - /// - public override void SetValue(int fieldIndex, object fieldValue) => throw Exceptions.ObjectIsReadOnly(null); - - protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) - { - if (isWriting) - throw Exceptions.ObjectIsReadOnly(null); - return origin.GetMappedContainer(fieldIndex, isWriting); - } - - /// - public override bool Equals(Tuple other) => origin.Equals(other); - - /// - public override int GetHashCode() => origin.GetHashCode(); - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Tuple to provide read-only wrapper for. - public ReadOnlyTransformTuple(Tuple tuple) - { - ArgumentValidator.EnsureArgumentNotNull(tuple, nameof(tuple)); - origin = tuple; - } - } -} diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index aa056e632d..c3358d92d8 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -23,7 +23,6 @@ namespace Xtensive.Tuples.Transform [Serializable] public class MapTransform : TupleTransformBase { - private readonly bool isReadOnly; private int sourceCount; internal IReadOnlyList singleSourceMap; internal Pair[] map; @@ -33,12 +32,6 @@ public class MapTransform : TupleTransformBase /// public const int NoMapping = int.MinValue; - /// - public override bool IsReadOnly { - [DebuggerStepThrough] - get { return isReadOnly; } - } - /// /// Gets the count of source this transform maps to the target one. /// @@ -239,7 +232,7 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple sou /// public override string ToString() { - string description = $"{SourceCount}: {(SourceCount == 1 ? singleSourceMap.ToCommaDelimitedString() : map.ToCommaDelimitedString())}, {(isReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + string description = $"{SourceCount}: {(SourceCount == 1 ? singleSourceMap.ToCommaDelimitedString() : map.ToCommaDelimitedString())}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; return string.Format(Strings.TupleTransformFormat, GetType().GetShortName(), description); @@ -251,7 +244,7 @@ public override string ToString() /// /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Initial property value. /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[] map) @@ -263,7 +256,7 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[ /// /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Initial property value. /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) @@ -275,13 +268,11 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Initial property value. protected MapTransform(bool isReadOnly, TupleDescriptor descriptor) - : base(descriptor) + : base(descriptor, isReadOnly) { - ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); - this.isReadOnly = isReadOnly; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs deleted file mode 100644 index 6f98ea58c7..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Yakunin -// Created: 2008.06.02 - -using System; -using System.Diagnostics; -using System.Linq; -using Xtensive.Core; - -using Xtensive.Tuples.Transform.Internals; - -namespace Xtensive.Tuples.Transform -{ - /// - /// Describes read-only tuple transformation. - /// - [Serializable] - public sealed class ReadOnlyTransform : TupleTransformBase - { - private static readonly ReadOnlyTransform instance = new ReadOnlyTransform(); - - /// - /// Gets the only instance of this type. - /// - public static ReadOnlyTransform Instance { - [DebuggerStepThrough] - get { return instance; } - } - - /// - /// - /// Implementation in this class always returns . - /// - public override bool IsReadOnly { - [DebuggerStepThrough] - get { - return true; - } - } - - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// Transformation argument. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, Tuple source) - { - switch (transformType) { - case TupleTransformType.Auto: - // TODO: Implement "Auto" for generated read-only tuples, when they'll be ready - case TupleTransformType.TransformedTuple: - if (source is ReadOnlyTransformTuple) - return source; - return new ReadOnlyTransformTuple(source); - case TupleTransformType.Tuple: - // TODO: Return generated read-only tuple copy - return new ReadOnlyTransformTuple(source.ToRegular()); - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); - } - } - - - // Constructors - - private ReadOnlyTransform() - : base(null) - { - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 7eae54749e..7e46e8d46a 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -61,7 +61,7 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDesc /// /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Source tuple descriptor. /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs index c1ea3b441f..c02c996021 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs @@ -5,6 +5,7 @@ // Created: 2008.04.30 using System; +using Xtensive.Core; using Xtensive.Reflection; namespace Xtensive.Tuples.Transform @@ -15,35 +16,25 @@ namespace Xtensive.Tuples.Transform [Serializable] public abstract class TupleTransformBase { - private Tuple defaultResult; - /// /// Gets describing the tuples /// this transform may produce. - /// means "any" (i.e. transform definition - /// is not descriptor-dependent). /// public TupleDescriptor Descriptor { get; } - /// - /// Gets the default result tuple. - /// Can be used to get default values for the result tuple fields. - /// Must be a read-only tuple. - /// - public Tuple DefaultResult => - Descriptor == null ? null : defaultResult ??= Tuple.Create(Descriptor).ToReadOnly(TupleTransformType.Tuple); - /// /// Indicates whether transform always produces read-only tuples or not. /// - public virtual bool IsReadOnly => false; + public bool IsReadOnly { get; } /// - public override string ToString() => GetType().GetShortName(); + public override string ToString() => $"[{GetType().GetShortName()}, {(IsReadOnly ? "readOnly" : string.Empty)}]"; - protected TupleTransformBase(TupleDescriptor descriptor) + protected TupleTransformBase(TupleDescriptor descriptor, bool isReadOnly) { + ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); Descriptor = descriptor; + IsReadOnly = isReadOnly; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index 6fed209a2c..f3724a7d67 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -346,19 +346,6 @@ public static RegularTuple ToRegular(this Tuple source) return result; } - /// - /// Converts tuple to read-only one. - /// - /// The tuple to convert to read-only. - /// The type of transformation to perform. - /// Read-only version of tuple. - public static Tuple ToReadOnly(this Tuple source, TupleTransformType transformType) - { - if (source==null) - return null; - return ReadOnlyTransform.Instance.Apply(transformType, source); - } - /// /// Converts tuple to fast read-only one. /// From 40621453848ff54da9582496b29eb4aa7787b32e Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 21:25:54 -0800 Subject: [PATCH 04/64] Refactor MapTransform class --- .../Tuples/Transform/MapTransform.cs | 238 ++++++++---------- 1 file changed, 110 insertions(+), 128 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index c3358d92d8..b2f18f9f17 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -44,7 +44,8 @@ public int SourceCount /// /// Gets or sets destination-to-source field map for the first source only. /// - public IReadOnlyList SingleSourceMap { + public IReadOnlyList SingleSourceMap + { [DebuggerStepThrough] get => singleSourceMap; } @@ -58,43 +59,6 @@ public IReadOnlyList> Map get { return Array.AsReadOnly(map); } } - protected void SetSingleSourceMap(IReadOnlyList singleSourceMap) - { - ArgumentValidator.EnsureArgumentNotNull(singleSourceMap, nameof(singleSourceMap)); - var newMap = new Pair[Descriptor.Count]; - var index = 0; - for (; index < newMap.Length && index < singleSourceMap.Count; index++) { - newMap[index] = new Pair(0, singleSourceMap[index]); - } - while (index < newMap.Length) { - newMap[index++] = new Pair(0, NoMapping); - } - - map = newMap; - this.singleSourceMap = singleSourceMap; - sourceCount = 1; - } - - protected void SetMap(Pair[] map) - { - ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); - int[] newFirstSourceMap = new int[map.Length]; - int index = 0; - int newSourceCount = -1; - foreach (var mappedTo in map) { - if (mappedTo.First>newSourceCount) - newSourceCount = mappedTo.First; - newFirstSourceMap[index++] = mappedTo.First==0 ? mappedTo.Second : -1; - } - newSourceCount++; - this.map = map; - if (newSourceCount==1) - singleSourceMap = newFirstSourceMap; - else - singleSourceMap = null; - sourceCount = newSourceCount; - } - /// /// Applies the transformation. /// @@ -105,32 +69,33 @@ protected void SetMap(Pair[] map) /// dependently on specified . public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) { - ArgumentValidator.EnsureArgumentNotNull(sources, "sources"); - if (sourceCount>sources.Length) + ArgumentValidator.EnsureArgumentNotNull(sources, nameof(sources)); + if (sourceCount > sources.Length) { throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); + } switch (sourceCount) { - case 1: - return Apply(transformType, sources[0]); - case 2: - return Apply(transformType, sources[0], sources[1]); - case 3: - return Apply(transformType, sources[0], sources[1], sources[2]); - default: - switch (transformType) { - case TupleTransformType.Auto: - foreach (Tuple tuple in sources) - if (tuple is TransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple(this, sources); - case TupleTransformType.Tuple: - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; + case 1: + return Apply(transformType, sources[0]); + case 2: + return Apply(transformType, sources[0], sources[1]); + case 3: + return Apply(transformType, sources[0], sources[1], sources[2]); default: - throw new ArgumentOutOfRangeException("transformType"); - } + switch (transformType) { + case TupleTransformType.Auto: + foreach (Tuple tuple in sources) + if (tuple is TransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new MapTransformTuple(this, sources); + case TupleTransformType.Tuple: + Tuple result = Tuple.Create(Descriptor); + sources.CopyTo(result, map); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); + } } } @@ -144,21 +109,21 @@ public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) /// dependently on specified . protected Tuple Apply(TupleTransformType transformType, Tuple source) { - if (sourceCount>1) + if (sourceCount > 1) throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); switch (transformType) { - case TupleTransformType.Auto: - if (source is TransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple1(this, source); - case TupleTransformType.Tuple: - Tuple result = Tuple.Create(Descriptor); - source.CopyTo(result, singleSourceMap); - return result; - default: - throw new ArgumentOutOfRangeException("transformType"); + case TupleTransformType.Auto: + if (source is TransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new MapTransformTuple1(this, source); + case TupleTransformType.Tuple: + Tuple result = Tuple.Create(Descriptor); + source.CopyTo(result, singleSourceMap); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); } } @@ -173,24 +138,24 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source) /// dependently on specified . protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) { - if (sourceCount>2) + if (sourceCount > 2) throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); switch (transformType) { - case TupleTransformType.Auto: - if (source1 is TransformedTuple) - goto case TupleTransformType.Tuple; - if (source2 is TransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple3(this, source1, source2); - case TupleTransformType.Tuple: - FixedList3 sources = new FixedList3(source1, source2); - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; - default: - throw new ArgumentOutOfRangeException("transformType"); + case TupleTransformType.Auto: + if (source1 is TransformedTuple) + goto case TupleTransformType.Tuple; + if (source2 is TransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new MapTransformTuple3(this, source1, source2); + case TupleTransformType.Tuple: + FixedList3 sources = new FixedList3(source1, source2); + Tuple result = Tuple.Create(Descriptor); + sources.CopyTo(result, map); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); } } @@ -206,26 +171,27 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple sou /// dependently on specified . protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) { - if (sourceCount>3) + if (sourceCount > 3) { throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); + } switch (transformType) { - case TupleTransformType.Auto: - if (source1 is TransformedTuple) - goto case TupleTransformType.Tuple; - if (source2 is TransformedTuple) - goto case TupleTransformType.Tuple; - if (source3 is TransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple3(this, source1, source2, source3); - case TupleTransformType.Tuple: - FixedList3 sources = new FixedList3(source1, source2, source3); - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; - default: - throw new ArgumentOutOfRangeException("transformType"); + case TupleTransformType.Auto: + if (source1 is TransformedTuple) + goto case TupleTransformType.Tuple; + if (source2 is TransformedTuple) + goto case TupleTransformType.Tuple; + if (source3 is TransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new MapTransformTuple3(this, source1, source2, source3); + case TupleTransformType.Tuple: + FixedList3 sources = new FixedList3(source1, source2, source3); + Tuple result = Tuple.Create(Descriptor); + sources.CopyTo(result, map); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); } } @@ -233,12 +199,12 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple sou public override string ToString() { string description = $"{SourceCount}: {(SourceCount == 1 ? singleSourceMap.ToCommaDelimitedString() : map.ToCommaDelimitedString())}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), + return string.Format(Strings.TupleTransformFormat, + GetType().GetShortName(), description); } - + // Constructors /// @@ -248,31 +214,47 @@ public override string ToString() /// Initial property value. /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[] map) - : this(isReadOnly, descriptor) - { - SetMap(map); - } - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Initial property value. - /// property value. - public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) - : this(isReadOnly, descriptor) + : base(descriptor, isReadOnly) { - SetSingleSourceMap(map); + ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); + var newFirstSourceMap = new int[map.Length]; + var index = 0; + var newSourceCount = -1; + foreach (var mappedTo in map) { + if (mappedTo.First > newSourceCount) { + newSourceCount = mappedTo.First; + } + newFirstSourceMap[index++] = mappedTo.First == 0 ? mappedTo.Second : -1; + } + newSourceCount++; + + this.map = map; + singleSourceMap = newSourceCount == 1 ? newFirstSourceMap : null; + sourceCount = newSourceCount; } - + /// /// Initializes a new instance of this type. /// /// property value. /// Initial property value. - protected MapTransform(bool isReadOnly, TupleDescriptor descriptor) + /// property value. + public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList singleSourceMap) : base(descriptor, isReadOnly) { - } + ArgumentValidator.EnsureArgumentNotNull(singleSourceMap, nameof(singleSourceMap)); + var newMap = new Pair[Descriptor.Count]; + var index = 0; + for (; index < newMap.Length && index < singleSourceMap.Count; index++) { + newMap[index] = new Pair(0, singleSourceMap[index]); + } + while (index < newMap.Length) { + newMap[index++] = new Pair(0, NoMapping); + } + + map = newMap; + this.singleSourceMap = singleSourceMap; + sourceCount = 1; + } } } \ No newline at end of file From cbc605c34f96d5a296808f2c5aa892cf9eec11ef Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 21:38:33 -0800 Subject: [PATCH 05/64] Directly call specific Apply method to avoid params allocations --- .../Transform/Internals/MapTransformTuple.cs | 2 -- .../Tuples/Transform/MapTransform.cs | 31 ++++++------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index 91440428d6..c6d7edd45f 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -5,8 +5,6 @@ // Created: 2008.05.07 using System; -using System.Collections.Generic; -using System.Diagnostics; using Xtensive.Core; diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index b2f18f9f17..c2bc4e8d09 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Xtensive.Collections; using Xtensive.Core; using Xtensive.Reflection; @@ -35,29 +34,17 @@ public class MapTransform : TupleTransformBase /// /// Gets the count of source this transform maps to the target one. /// - public int SourceCount - { - [DebuggerStepThrough] - get { return sourceCount; } - } + public int SourceCount => sourceCount; /// /// Gets or sets destination-to-source field map for the first source only. /// - public IReadOnlyList SingleSourceMap - { - [DebuggerStepThrough] - get => singleSourceMap; - } + public IReadOnlyList SingleSourceMap => singleSourceMap; /// /// Gets or sets destination-to-source field map. /// - public IReadOnlyList> Map - { - [DebuggerStepThrough] - get { return Array.AsReadOnly(map); } - } + public IReadOnlyList> Map => map; /// /// Applies the transformation. @@ -107,10 +94,11 @@ public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) /// Transformation result - /// either or descendant, /// dependently on specified . - protected Tuple Apply(TupleTransformType transformType, Tuple source) + public Tuple Apply(TupleTransformType transformType, Tuple source) { - if (sourceCount > 1) + if (sourceCount > 1) { throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); + } switch (transformType) { case TupleTransformType.Auto: if (source is TransformedTuple) @@ -136,10 +124,11 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source) /// Transformation result - /// either or descendant, /// dependently on specified . - protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) + public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) { - if (sourceCount > 2) + if (sourceCount > 2) { throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); + } switch (transformType) { case TupleTransformType.Auto: if (source1 is TransformedTuple) @@ -169,7 +158,7 @@ protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple sou /// Transformation result - /// either or descendant, /// dependently on specified . - protected Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) + public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) { if (sourceCount > 3) { throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); From ea983d0edf0e0ea472179ba8a340cac8172288b8 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 22:18:59 -0800 Subject: [PATCH 06/64] Convert the TransoformedTuple abstract class to a marker interface --- .../Tuples/Interfaces/ITransformedTuple.cs | 17 +++++++++++ .../Tuples/Transform/MapTransform.cs | 22 +++++++------- .../Tuples/Transform/TransformedTuple.cs | 29 ------------------- .../TransformedTuple{TTupleTransform}.cs | 2 +- 4 files changed, 29 insertions(+), 41 deletions(-) create mode 100644 Orm/Xtensive.Orm/Tuples/Interfaces/ITransformedTuple.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs diff --git a/Orm/Xtensive.Orm/Tuples/Interfaces/ITransformedTuple.cs b/Orm/Xtensive.Orm/Tuples/Interfaces/ITransformedTuple.cs new file mode 100644 index 0000000000..407c603320 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Interfaces/ITransformedTuple.cs @@ -0,0 +1,17 @@ +// Copyright (C) 2003-2010 Xtensive LLC. +// All rights reserved. +// For conditions of distribution and use, see license. +// Created by: Alexey Kochetov +// Created: 2008.05.07 + +using System; + +namespace Xtensive.Tuples.Transform +{ + /// + /// Marker interface for all transformed tuples. + /// + public interface ITransformedTuple: ITuple + { + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index c2bc4e8d09..3a54a51f09 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -52,7 +52,7 @@ public class MapTransform : TupleTransformBase /// The type of transformation to perform. /// Transformation sources. /// Transformation result - - /// either or descendant, + /// either or descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) { @@ -71,7 +71,7 @@ public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) switch (transformType) { case TupleTransformType.Auto: foreach (Tuple tuple in sources) - if (tuple is TransformedTuple) + if (tuple is ITransformedTuple) goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: @@ -92,7 +92,7 @@ public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) /// The type of transformation to perform. /// Transformation source. /// Transformation result - - /// either or descendant, + /// either or descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { @@ -101,7 +101,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source) } switch (transformType) { case TupleTransformType.Auto: - if (source is TransformedTuple) + if (source is ITransformedTuple) goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: @@ -122,7 +122,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source) /// First transformation source. /// Second transformation source. /// Transformation result - - /// either or descendant, + /// either or descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) { @@ -131,9 +131,9 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source } switch (transformType) { case TupleTransformType.Auto: - if (source1 is TransformedTuple) + if (source1 is ITransformedTuple) goto case TupleTransformType.Tuple; - if (source2 is TransformedTuple) + if (source2 is ITransformedTuple) goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: @@ -156,7 +156,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source /// Second transformation source. /// Third transformation source. /// Transformation result - - /// either or descendant, + /// either or descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) { @@ -165,11 +165,11 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source } switch (transformType) { case TupleTransformType.Auto: - if (source1 is TransformedTuple) + if (source1 is ITransformedTuple) goto case TupleTransformType.Tuple; - if (source2 is TransformedTuple) + if (source2 is ITransformedTuple) goto case TupleTransformType.Tuple; - if (source3 is TransformedTuple) + if (source3 is ITransformedTuple) goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs deleted file mode 100644 index faebb970c1..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.05.07 - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for any transformed tuples. - /// - [Serializable] - public abstract class TransformedTuple : Tuple - { - // /// - // /// Gets the transform used to produce this instance. - // /// - // public abstract bool IsReadOnly { get; } - - /// - public abstract override TupleDescriptor Descriptor { get; } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs index 49a655e1cf..2d2e0042f0 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform /// Base class for all transformed tuples. /// [Serializable] - public abstract class TransformedTuple : TransformedTuple + public abstract class TransformedTuple : Tuple, ITransformedTuple where TTupleTransform : TupleTransformBase { private TTupleTransform transform; From 0fd5a9dac933ea58a5e0d52a4528c8b8fa6d1404 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 22:32:30 -0800 Subject: [PATCH 07/64] Improve the MapTransformTuple1 code --- .../Transform/Internals/MapTransformTuple1.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index 370e2fe727..241ae1f23a 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -14,15 +14,14 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - public sealed class MapTransformTuple1 : TransformedTuple + internal sealed class MapTransformTuple1 : TransformedTuple { - private Tuple tuple; - + private readonly Tuple source; private Tuple defaultResult; + /// /// Gets the default result tuple. /// Can be used to get default values for the result tuple fields. - /// Must be a read-only tuple. /// private Tuple DefaultResult => defaultResult ??= Tuple.Create(TypedTransform.Descriptor); @@ -31,16 +30,17 @@ public sealed class MapTransformTuple1 : TransformedTuple /// public override TupleFieldState GetFieldState(int fieldIndex) { - int index = GetMappedFieldIndex(fieldIndex); - return index == MapTransform.NoMapping ? TupleFieldState.Default : tuple.GetFieldState(index); + var index = GetMappedFieldIndex(fieldIndex); + return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - int index = GetMappedFieldIndex(fieldIndex); - if (index == MapTransform.NoMapping) + var index = GetMappedFieldIndex(fieldIndex); + if (index == MapTransform.NoMapping) { return; - tuple.SetFieldState(index, fieldState); + } + source.SetFieldState(index, fieldState); } /// @@ -49,33 +49,33 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) int index = GetMappedFieldIndex(fieldIndex); return index == MapTransform.NoMapping ? DefaultResult.GetValue(fieldIndex, out fieldState) - : tuple.GetValue(index, out fieldState); + : source.GetValue(index, out fieldState); } /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) + if (TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); - tuple.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); + } + source.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); } #endregion private int GetMappedFieldIndex(int fieldIndex) { - int mappedIndex = TypedTransform.singleSourceMap[fieldIndex]; - return mappedIndex < 0 ? MapTransform.NoMapping :mappedIndex; + var mappedIndex = TypedTransform.singleSourceMap[fieldIndex]; + return mappedIndex < 0 ? MapTransform.NoMapping : mappedIndex; } protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); + } var index = GetMappedFieldIndex(fieldIndex); - return index == MapTransform.NoMapping - ? new Pair() - : tuple.GetMappedContainer(index, isWriting); + return index == MapTransform.NoMapping ? default : source.GetMappedContainer(index, isWriting); } @@ -89,7 +89,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, public MapTransformTuple1(MapTransform transform, Tuple source) : base(transform) { - tuple = source; + this.source = source; } } } \ No newline at end of file From 376ca6182ed03ce3124a436927f05adbce75571d Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 22:36:17 -0800 Subject: [PATCH 08/64] Improve code of the MapTransformTuple3 class --- .../Transform/Internals/MapTransformTuple3.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs index 4bc84b710e..ca37cf0253 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs @@ -15,9 +15,9 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping up to 3 source tuples to a single one (this). /// [Serializable] - public sealed class MapTransformTuple3 : TransformedTuple + internal sealed class MapTransformTuple3 : TransformedTuple { - private FixedList3 tuples; + private readonly FixedList3 tuples; #region GetFieldState, GetValue, SetValue methods @@ -37,16 +37,17 @@ protected internal override void SetFieldState(int fieldIndex, TupleFieldState f /// public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - Pair indexes = TypedTransform.map[fieldIndex]; + var indexes = TypedTransform.map[fieldIndex]; return tuples[indexes.First].GetValue(indexes.Second, out fieldState); } /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) + if (TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); - Pair indexes = TypedTransform.map[fieldIndex]; + } + var indexes = TypedTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); } @@ -54,8 +55,9 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); + } var map = TypedTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); } From 4beb25a20c98a60e42e12ddda59b1f924d1b8bbc Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 22:39:38 -0800 Subject: [PATCH 09/64] Improve code of the MapTransformTuple class --- .../Tuples/Transform/Internals/MapTransformTuple.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index c6d7edd45f..ea031e66f2 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping arbitrary count of source tuples to a single one (this). /// [Serializable] - public sealed class MapTransformTuple : TransformedTuple + internal sealed class MapTransformTuple : TransformedTuple { private readonly Tuple[] tuples; @@ -43,8 +43,9 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) + if (TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); + } var indexes = TypedTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); } @@ -53,8 +54,9 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) + if (isWriting && TypedTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); + } var map = TypedTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); } From 4e268b6e375921b12a92b3ed3f42cb51b797d65e Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 22 Dec 2021 23:48:22 -0800 Subject: [PATCH 10/64] Replace FixedList3 with internal FixedReadOnlyList3 --- Orm/Xtensive.Orm/Collections/FixedList3.cs | 184 ------------------ .../Internals/FixedReadOnlyList3.cs | 84 ++++++++ .../Transform/Internals/MapTransformTuple3.cs | 6 +- .../Tuples/Transform/MapTransform.cs | 4 +- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 30 ++- 5 files changed, 103 insertions(+), 205 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Collections/FixedList3.cs create mode 100644 Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs diff --git a/Orm/Xtensive.Orm/Collections/FixedList3.cs b/Orm/Xtensive.Orm/Collections/FixedList3.cs deleted file mode 100644 index c7a0539ca3..0000000000 --- a/Orm/Xtensive.Orm/Collections/FixedList3.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2007.10.20 - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Xtensive.Core; - - -namespace Xtensive.Collections -{ - /// - /// Defines a fixed stack-like list with three items. - /// - /// Type of items. - [Serializable] - [DebuggerDisplay("Count = {Count}")] - public struct FixedList3 - { - private T slot1; - private T slot2; - private T slot3; - private int count; - - /// - /// Gets or sets the element at the specified index. - /// - /// Index of the item - /// The index is greater or equal count of items. - public T this[int index] - { - get - { - if (index<0 || index>=count) - ArgumentValidator.EnsureArgumentIsInRange(index, 0, count-1, "index"); - switch (index) { - case 0: - return slot1; - case 1: - return slot2; - default: - return slot3; - } - } - set - { - if (index<0 || index>=count) - ArgumentValidator.EnsureArgumentIsInRange(index, 0, count-1, "index"); - switch (index) { - case 0: - slot1 = value; - break; - case 1: - slot2 = value; - break; - default: - slot3 = value; - break; - } - } - } - - /// - /// Enumerates all items. - /// - public IEnumerable Items - { - get - { - if (count > 0) { - yield return slot1; - if (count > 1){ - yield return slot2; - if (count > 2) - yield return slot3; - } - } - } - } - - /// - /// Gets count of items. - /// - public int Count - { - [DebuggerStepThrough] - get { return count; } - } - - /// - /// Adds item to the list. - /// - /// Item to add. - /// The list already have three items. - public void Push(T item) - { - switch (count) { - case 0: - slot1 = item; - break; - case 1: - slot2 = item; - break; - case 2: - slot3 = item; - break; - default: - ArgumentValidator.EnsureArgumentIsInRange(count, 0, 2, "count"); - return; - } - count++; - } - - /// - /// Removes latest item from the . - /// - public T Pop() - { - T result = default(T); - switch (count) { - case 1: - result = slot1; - slot1 = default(T); - count--; - break; - case 2: - result = slot2; - slot2 = default(T); - count--; - break; - case 3: - result = slot3; - slot3 = default(T); - count--; - break; - } - return result; - } - - /// - /// Initializes a new instance of this type. - /// - /// Item to add. - public FixedList3(T item) - { - slot1 = item; - slot2 = default(T); - slot3 = default(T); - count = 1; - } - - - /// - /// Initializes a new instance of this type. - /// - /// First item to add. - /// Second item ot add. - public FixedList3(T first, T second) - { - slot1 = first; - slot2 = second; - slot3 = default(T); - count = 2; - } - - /// - /// Initializes a new instance of this type. - /// - /// First item to add. - /// Second item ot add. - /// Third item to add. - public FixedList3(T first, T second, T third) - { - slot1 = first; - slot2 = second; - slot3 = third; - count = 3; - } - - } -} diff --git a/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs b/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs new file mode 100644 index 0000000000..8426b58454 --- /dev/null +++ b/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs @@ -0,0 +1,84 @@ +// Copyright (C) 2003-2010 Xtensive LLC. +// All rights reserved. +// For conditions of distribution and use, see license. +// Created by: Alexey Kochetov +// Created: 2007.10.20 + +using System; +using System.Diagnostics; +using Xtensive.Core; + + +namespace Xtensive.Collections +{ + /// + /// Defines a fixed stack-like list with three items. + /// + /// Type of items. + [Serializable] + [DebuggerDisplay("Count = {Count}")] + internal readonly struct FixedReadOnlyList3 + { + private readonly (T n1, T n2, T n3) slots; + private readonly int count; + + /// + /// Gets count of items. + /// + public int Count => count; + + /// + /// Gets or sets the element at the specified index. + /// + /// Index of the item + /// The index is greater or equal count of items. + public T this[int index] + { + get { + if (index < 0 || index >= count) { + ArgumentValidator.EnsureArgumentIsInRange(index, 0, count - 1, nameof(index)); + } + return index switch { + 0 => slots.n1, + 1 => slots.n2, + _ => slots.n3 + }; + } + } + + + /// + /// Initializes a new instance of this type. + /// + /// Item to add. + public FixedReadOnlyList3(T item) + { + slots = (item, default, default); + count = 1; + } + + + /// + /// Initializes a new instance of this type. + /// + /// First item to add. + /// Second item ot add. + public FixedReadOnlyList3(T first, T second) + { + slots = (first, second, default); + count = 2; + } + + /// + /// Initializes a new instance of this type. + /// + /// First item to add. + /// Second item ot add. + /// Third item to add. + public FixedReadOnlyList3(T first, T second, T third) + { + slots = (first, second, third); + count = 3; + } + } +} diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs index ca37cf0253..363e9572ff 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs @@ -17,7 +17,7 @@ namespace Xtensive.Tuples.Transform.Internals [Serializable] internal sealed class MapTransformTuple3 : TransformedTuple { - private readonly FixedList3 tuples; + private readonly FixedReadOnlyList3 tuples; #region GetFieldState, GetValue, SetValue methods @@ -74,7 +74,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, public MapTransformTuple3(MapTransform transform, Tuple source1, Tuple source2) : base(transform) { - tuples = new FixedList3(source1, source2); + tuples = new FixedReadOnlyList3(source1, source2); } /// @@ -87,7 +87,7 @@ public MapTransformTuple3(MapTransform transform, Tuple source1, Tuple source2) public MapTransformTuple3(MapTransform transform, Tuple source1, Tuple source2, Tuple source3) : base(transform) { - tuples = new FixedList3(source1, source2, source3); + tuples = new FixedReadOnlyList3(source1, source2, source3); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 3a54a51f09..fa31a058e6 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -139,7 +139,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source case TupleTransformType.TransformedTuple: return new MapTransformTuple3(this, source1, source2); case TupleTransformType.Tuple: - FixedList3 sources = new FixedList3(source1, source2); + var sources = new FixedReadOnlyList3(source1, source2); Tuple result = Tuple.Create(Descriptor); sources.CopyTo(result, map); return result; @@ -175,7 +175,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source case TupleTransformType.TransformedTuple: return new MapTransformTuple3(this, source1, source2, source3); case TupleTransformType.Tuple: - FixedList3 sources = new FixedList3(source1, source2, source3); + var sources = new FixedReadOnlyList3(source1, source2, source3); Tuple result = Tuple.Create(Descriptor); sources.CopyTo(result, map); return result; diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index f3724a7d67..fd0bdd6fb0 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -149,19 +149,15 @@ public static void CopyTo(this Tuple[] sources, Tuple target, Pair[] m /// Tuple that receives the data. /// Target-to-source field index map. /// Negative value in this map means "skip this element". - public static void CopyTo(this FixedList3 sources, Tuple target, Pair[] map) + internal static void CopyTo(this in FixedReadOnlyList3 sources, Tuple target, Pair[] map) { - var haveSlowSource = false; - var packedSources = new FixedList3(); - - for (int i = 0; i < sources.Count; i++) { - var packedSource = sources[i] as PackedTuple; - if (packedSource==null) { - haveSlowSource = true; - break; - } - packedSources.Push(packedSource); - } + var (packedSources, haveSlowSource) = sources.Count switch { + 1 => sources[0] is PackedTuple first ? (new FixedReadOnlyList3(first), false) : (default, true), + 2 => sources[0] is PackedTuple first && sources[1] is PackedTuple second ? (new FixedReadOnlyList3(first, second), false) : (default, true), + 3 => sources[0] is PackedTuple first && sources[1] is PackedTuple second && sources[2] is PackedTuple third + ? (new FixedReadOnlyList3(first, second, third), false) : (default, true), + _ => (default, false) + }; if (!haveSlowSource) { var packedTarget = target as PackedTuple; @@ -499,25 +495,27 @@ private static void CopyTupleArrayWithMappingFast(PackedTuple[] sources, PackedT } } - private static void Copy3TuplesWithMappingSlow(FixedList3 sources, Tuple target, Pair[] map) + private static void Copy3TuplesWithMappingSlow(in FixedReadOnlyList3 sources, Tuple target, Pair[] map) { for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { var sourceInfo = map[targetIndex]; var sourceTupleIndex = sourceInfo.First; var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) + if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) { CopyValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); + } } } - private static void Copy3TuplesWithMappingFast(FixedList3 sources, PackedTuple target, Pair[] map) + private static void Copy3TuplesWithMappingFast(in FixedReadOnlyList3 sources, PackedTuple target, Pair[] map) { for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { var sourceInfo = map[targetIndex]; var sourceTupleIndex = sourceInfo.First; var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) + if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) { CopyPackedValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); + } } } From 1a0b3a14a1e2d93ac4b6b204c939bfaf41d4e6ba Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 00:04:11 -0800 Subject: [PATCH 11/64] Rename TypedTransform property to TupleTransform --- .../Transform/Internals/MapTransformTuple.cs | 16 +++++++------- .../Transform/Internals/MapTransformTuple1.cs | 8 +++---- .../Transform/Internals/MapTransformTuple3.cs | 14 ++++++------- .../TransformedTuple{TTupleTransform}.cs | 21 ++++++------------- 4 files changed, 25 insertions(+), 34 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index ea031e66f2..a3df3343f0 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -23,30 +23,30 @@ internal sealed class MapTransformTuple : TransformedTuple /// public override TupleFieldState GetFieldState(int fieldIndex) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; return tuples[indexes.First].GetFieldState(indexes.Second); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; tuples[indexes.First].SetFieldState(indexes.Second, fieldState); } /// public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; return tuples[indexes.First].GetValue(indexes.Second, out fieldState); } /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) { + if (TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); } @@ -54,10 +54,10 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) { + if (isWriting && TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - var map = TypedTransform.map[fieldIndex]; + var map = TupleTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); } @@ -69,7 +69,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// /// The transform. /// Source tuples. - public MapTransformTuple(MapTransform transform, params Tuple[] sources) + public MapTransformTuple(MapTransform transform, Tuple[] sources) : base(transform) { ArgumentValidator.EnsureArgumentNotNull(sources, "tuples"); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index 241ae1f23a..427d0c6025 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -23,7 +23,7 @@ internal sealed class MapTransformTuple1 : TransformedTuple /// Gets the default result tuple. /// Can be used to get default values for the result tuple fields. /// - private Tuple DefaultResult => defaultResult ??= Tuple.Create(TypedTransform.Descriptor); + private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); #region GetFieldState, GetValue, SetValue methods @@ -55,7 +55,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) { + if (TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } source.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); @@ -65,13 +65,13 @@ public override void SetValue(int fieldIndex, object fieldValue) private int GetMappedFieldIndex(int fieldIndex) { - var mappedIndex = TypedTransform.singleSourceMap[fieldIndex]; + var mappedIndex = TupleTransform.singleSourceMap[fieldIndex]; return mappedIndex < 0 ? MapTransform.NoMapping : mappedIndex; } protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) { + if (isWriting && TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } var index = GetMappedFieldIndex(fieldIndex); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs index 363e9572ff..82a69ee555 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs @@ -24,30 +24,30 @@ internal sealed class MapTransformTuple3 : TransformedTuple /// public override TupleFieldState GetFieldState(int fieldIndex) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; return tuples[indexes.First].GetFieldState(indexes.Second); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; tuples[indexes.First].SetFieldState(indexes.Second, fieldState); } /// public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; return tuples[indexes.First].GetValue(indexes.Second, out fieldState); } /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TypedTransform.IsReadOnly) { + if (TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - var indexes = TypedTransform.map[fieldIndex]; + var indexes = TupleTransform.map[fieldIndex]; tuples[indexes.First].SetValue(indexes.Second, fieldValue); } @@ -55,10 +55,10 @@ public override void SetValue(int fieldIndex, object fieldValue) protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TypedTransform.IsReadOnly) { + if (isWriting && TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - var map = TypedTransform.map[fieldIndex]; + var map = TupleTransform.map[fieldIndex]; return tuples[map.First].GetMappedContainer(map.Second, isWriting); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs index 2d2e0042f0..e2ebbae53c 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs @@ -17,27 +17,18 @@ namespace Xtensive.Tuples.Transform public abstract class TransformedTuple : Tuple, ITransformedTuple where TTupleTransform : TupleTransformBase { - private TTupleTransform transform; - - /// - public override TupleDescriptor Descriptor => transform.Descriptor; - /// /// Gets or sets the transform used to produce this instance. /// - public TTupleTransform TypedTransform { - get { return transform; } - protected set { - transform = value; - } - } + public TTupleTransform TupleTransform { get; } + + /// + public override TupleDescriptor Descriptor => TupleTransform.Descriptor; /// public override string ToString() { - return string.Format(Strings.TransformedTupleFormat, - base.ToString(), - transform, string.Empty); + return string.Format(Strings.TransformedTupleFormat, base.ToString(), TupleTransform, string.Empty); } // Constructors @@ -48,7 +39,7 @@ public override string ToString() /// Tuple transform. protected TransformedTuple(TTupleTransform transform) { - this.transform = transform; + TupleTransform = transform; } } } \ No newline at end of file From 40b5c3f231e8571b0a5e829e62ef3333013d0f7a Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 17:19:38 -0800 Subject: [PATCH 12/64] Introduce better solution for readonly access to PackedFieldDescriptor structure by reference --- Orm/Xtensive.Orm/Tuples/AccessorDelegates.cs | 10 +- .../Tuples/Packed/PackedFieldAccessor.cs | 201 +++++++++--------- .../PackedFieldDescriptiorExtensions.cs | 19 ++ .../Tuples/Packed/PackedFieldDescriptor.cs | 23 +- Orm/Xtensive.Orm/Tuples/Packed/PackedTuple.cs | 76 +++---- Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs | 4 +- Orm/Xtensive.Orm/Tuples/Tuple.cs | 18 +- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 31 ++- 8 files changed, 197 insertions(+), 185 deletions(-) create mode 100644 Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptiorExtensions.cs diff --git a/Orm/Xtensive.Orm/Tuples/AccessorDelegates.cs b/Orm/Xtensive.Orm/Tuples/AccessorDelegates.cs index 031fe946d6..814a4e3f92 100644 --- a/Orm/Xtensive.Orm/Tuples/AccessorDelegates.cs +++ b/Orm/Xtensive.Orm/Tuples/AccessorDelegates.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alexis Kochetov // Created: 2009.09.17 @@ -16,7 +16,7 @@ namespace Xtensive.Tuples /// Field descriptor. /// State of a field. /// - internal delegate TValue GetValueDelegate(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState); + internal delegate TValue GetValueDelegate(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState); /// /// Incapsulates method. @@ -25,5 +25,5 @@ namespace Xtensive.Tuples /// Tuple to use. /// Field descriptor. /// A value. - internal delegate void SetValueDelegate(PackedTuple tuple, ref PackedFieldDescriptor descriptor, TValue value); + internal delegate void SetValueDelegate(PackedTuple tuple, in PackedFieldDescriptor descriptor, TValue value); } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs index 532c1cb645..244f4cca87 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs @@ -34,41 +34,46 @@ internal abstract class PackedFieldAccessor public readonly int ValueBitCount; protected readonly long ValueBitMask; - public void SetValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, bool isNullable, T value) + public void SetValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, bool isNullable, T value) { var setter = (isNullable ? NullableSetter : Setter) as SetValueDelegate; - if (setter!=null) - setter.Invoke(tuple, ref descriptor, value); - else - SetUntypedValue(tuple, ref descriptor, value); + if (setter != null) { + setter.Invoke(tuple, descriptor, value); + } + else { + SetUntypedValue(tuple, descriptor, value); + } } - public T GetValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, bool isNullable, out TupleFieldState fieldState) + public T GetValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, bool isNullable, out TupleFieldState fieldState) { var getter = (isNullable ? NullableGetter : Getter) as GetValueDelegate; - if (getter!=null) - return getter.Invoke(tuple, ref descriptor, out fieldState); - var targetType = typeof (T); + if (getter != null) { + return getter.Invoke(tuple, descriptor, out fieldState); + } + var targetType = typeof(T); //Dirty hack of nullable enum reading - if (isNullable) + if (isNullable) { targetType = Nullable.GetUnderlyingType(targetType) ?? targetType; - if (targetType.IsEnum) - return (T) Enum.ToObject(targetType, GetUntypedValue(tuple, ref descriptor, out fieldState)); - return (T) GetUntypedValue(tuple, ref descriptor, out fieldState); + } + if (targetType.IsEnum) { + return (T) Enum.ToObject(targetType, GetUntypedValue(tuple, descriptor, out fieldState)); + } + return (T) GetUntypedValue(tuple, descriptor, out fieldState); } - public abstract object GetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState); + public abstract object GetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState); - public abstract void SetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, object value); + public abstract void SetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, object value); - public abstract void CopyValue(PackedTuple source, ref PackedFieldDescriptor sourceDescriptor, - PackedTuple target, ref PackedFieldDescriptor targetDescriptor); + public abstract void CopyValue(PackedTuple source, in PackedFieldDescriptor sourceDescriptor, + PackedTuple target, in PackedFieldDescriptor targetDescriptor); - public abstract bool ValueEquals(PackedTuple left, ref PackedFieldDescriptor leftDescriptor, - PackedTuple right, ref PackedFieldDescriptor rightDescriptor); + public abstract bool ValueEquals(PackedTuple left, in PackedFieldDescriptor leftDescriptor, + PackedTuple right, in PackedFieldDescriptor rightDescriptor); - public abstract int GetValueHashCode(PackedTuple tuple, ref PackedFieldDescriptor descriptor); + public abstract int GetValueHashCode(PackedTuple tuple, in PackedFieldDescriptor descriptor); protected PackedFieldAccessor(int rank) { @@ -92,39 +97,39 @@ protected PackedFieldAccessor(int rank) internal sealed class ObjectFieldAccessor : PackedFieldAccessor { - public override object GetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState) + public override object GetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState) { - var state = tuple.GetFieldState(ref descriptor); + var state = tuple.GetFieldState(descriptor); fieldState = state; - return state==TupleFieldState.Available ? tuple.Objects[descriptor.ObjectIndex] : null; + return state == TupleFieldState.Available ? tuple.Objects[descriptor.GetObjectIndex()] : null; } - public override void SetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, object value) + public override void SetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, object value) { - tuple.Objects[descriptor.ObjectIndex] = value; - if (value!=null) - tuple.SetFieldState(ref descriptor, TupleFieldState.Available); - else - tuple.SetFieldState(ref descriptor, TupleFieldState.Available | TupleFieldState.Null); + tuple.Objects[descriptor.GetObjectIndex()] = value; + if (value != null) { + tuple.SetFieldState(descriptor, TupleFieldState.Available); + } + else { + tuple.SetFieldState(descriptor, TupleFieldState.Available | TupleFieldState.Null); + } } - public override void CopyValue(PackedTuple source, ref PackedFieldDescriptor sourceDescriptor, - PackedTuple target, ref PackedFieldDescriptor targetDescriptor) + public override void CopyValue(PackedTuple source, in PackedFieldDescriptor sourceDescriptor, + PackedTuple target, in PackedFieldDescriptor targetDescriptor) { - target.Objects[targetDescriptor.ObjectIndex] = source.Objects[sourceDescriptor.ObjectIndex]; + target.Objects[targetDescriptor.GetObjectIndex()] = source.Objects[sourceDescriptor.GetObjectIndex()]; } - public override bool ValueEquals(PackedTuple left, ref PackedFieldDescriptor leftDescriptor, - PackedTuple right, ref PackedFieldDescriptor rightDescriptor) + public override bool ValueEquals(PackedTuple left, in PackedFieldDescriptor leftDescriptor, + PackedTuple right, in PackedFieldDescriptor rightDescriptor) { - var leftValue = left.Objects[leftDescriptor.ObjectIndex]; - var rightValue = right.Objects[rightDescriptor.ObjectIndex]; - return leftValue.Equals(rightValue); + return Equals(left.Objects[leftDescriptor.GetObjectIndex()], right.Objects[rightDescriptor.GetObjectIndex()]); } - public override int GetValueHashCode(PackedTuple tuple, ref PackedFieldDescriptor descriptor) + public override int GetValueHashCode(PackedTuple tuple, in PackedFieldDescriptor descriptor) { - return tuple.Objects[descriptor.ObjectIndex].GetHashCode(); + return tuple.Objects[descriptor.GetObjectIndex()]?.GetHashCode() ?? 0; } public ObjectFieldAccessor() @@ -148,7 +153,7 @@ private static int GetRank(int bitSize) protected ValueFieldAccessor(int bitCount) : base(GetRank(bitCount)) - {} + { } } internal abstract class ValueFieldAccessor : ValueFieldAccessor @@ -165,93 +170,91 @@ internal abstract class ValueFieldAccessor : ValueFieldAccessor protected virtual T Decode(long[] values, int offset) => throw new NotSupportedException(); - public override object GetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState) + public override object GetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState) { - var state = tuple.GetFieldState(ref descriptor); - fieldState = state; - return state==TupleFieldState.Available ? (object) Load(tuple, ref descriptor) : null; + fieldState = tuple.GetFieldState(descriptor); + return fieldState == TupleFieldState.Available ? (object) Load(tuple, descriptor) : null; } - public override void SetUntypedValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, object value) + public override void SetUntypedValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, object value) { - if (value!=null) { - Store(tuple, ref descriptor, (T) value); - tuple.SetFieldState(ref descriptor, TupleFieldState.Available); + if (value != null) { + Store(tuple, descriptor, (T) value); + tuple.SetFieldState(descriptor, TupleFieldState.Available); } else { - tuple.SetFieldState(ref descriptor, TupleFieldState.Available | TupleFieldState.Null); + tuple.SetFieldState(descriptor, TupleFieldState.Available | TupleFieldState.Null); } } - public override void CopyValue(PackedTuple source, ref PackedFieldDescriptor sourceDescriptor, - PackedTuple target, ref PackedFieldDescriptor targetDescriptor) + public override void CopyValue(PackedTuple source, in PackedFieldDescriptor sourceDescriptor, + PackedTuple target, in PackedFieldDescriptor targetDescriptor) { - Store(target, ref targetDescriptor, Load(source, ref sourceDescriptor)); + Store(target, targetDescriptor, Load(source, sourceDescriptor)); } - public override bool ValueEquals(PackedTuple left, ref PackedFieldDescriptor leftDescriptor, - PackedTuple right, ref PackedFieldDescriptor rightDescriptor) + public override bool ValueEquals(PackedTuple left, in PackedFieldDescriptor leftDescriptor, + PackedTuple right, in PackedFieldDescriptor rightDescriptor) { - var leftValue = Load(left, ref leftDescriptor); - var rightValue = Load(right, ref rightDescriptor); - return leftValue.Equals(rightValue); + return Load(left, leftDescriptor).Equals(Load(right, rightDescriptor)); } - public override int GetValueHashCode(PackedTuple tuple, ref PackedFieldDescriptor descriptor) + public override int GetValueHashCode(PackedTuple tuple, in PackedFieldDescriptor descriptor) { - return Load(tuple, ref descriptor).GetHashCode(); + return Load(tuple, descriptor).GetHashCode(); } - private T GetValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState) + private T GetValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState) { - var state = tuple.GetFieldState(ref descriptor); - fieldState = state; - return state==TupleFieldState.Available ? Load(tuple, ref descriptor) : DefaultValue; + fieldState = tuple.GetFieldState(descriptor); + return fieldState == TupleFieldState.Available ? Load(tuple, descriptor) : DefaultValue; } - private T? GetNullableValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, out TupleFieldState fieldState) + private T? GetNullableValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, out TupleFieldState fieldState) { - var state = tuple.GetFieldState(ref descriptor); - fieldState = state; - return state==TupleFieldState.Available ? Load(tuple, ref descriptor) : NullValue; + fieldState = tuple.GetFieldState(descriptor); + return fieldState == TupleFieldState.Available ? Load(tuple, descriptor) : NullValue; } - private void SetValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, T value) + private void SetValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, T value) { - Store(tuple, ref descriptor, value); - tuple.SetFieldState(ref descriptor, TupleFieldState.Available); + Store(tuple, descriptor, value); + tuple.SetFieldState(descriptor, TupleFieldState.Available); } - private void SetNullableValue(PackedTuple tuple, ref PackedFieldDescriptor descriptor, T? value) + private void SetNullableValue(PackedTuple tuple, in PackedFieldDescriptor descriptor, T? value) { - if (value!=null) { - Store(tuple, ref descriptor, value.Value); - tuple.SetFieldState(ref descriptor, TupleFieldState.Available); + if (value != null) { + Store(tuple, descriptor, value.Value); + tuple.SetFieldState(descriptor, TupleFieldState.Available); + } + else { + tuple.SetFieldState(descriptor, TupleFieldState.Available | TupleFieldState.Null); } - else - tuple.SetFieldState(ref descriptor, TupleFieldState.Available | TupleFieldState.Null); } - private void Store(PackedTuple tuple, ref PackedFieldDescriptor d, T value) + private void Store(PackedTuple tuple, in PackedFieldDescriptor d, T value) { + var valueIndex = d.GetValueIndex(); if (Rank > 6) { - Encode(value, tuple.Values, d.ValueIndex); + Encode(value, tuple.Values, valueIndex); return; } var encoded = Encode(value); - var block = tuple.Values[d.ValueIndex]; - var mask = ValueBitMask << d.ValueBitOffset; - tuple.Values[d.ValueIndex] = (block & ~mask) | ((encoded << d.ValueBitOffset) & mask); + var block = tuple.Values[valueIndex]; + var valueBitOffset = d.GetValueBitOffset(); + var mask = ValueBitMask << valueBitOffset; + tuple.Values[valueIndex] = (block & ~mask) | ((encoded << valueBitOffset) & mask); } - private T Load(PackedTuple tuple, ref PackedFieldDescriptor d) + private T Load(PackedTuple tuple, in PackedFieldDescriptor d) { if (Rank > 6) { - return Decode(tuple.Values, d.ValueIndex); + return Decode(tuple.Values, d.GetValueIndex()); } - var encoded = (tuple.Values[d.ValueIndex] >> d.ValueBitOffset) & ValueBitMask; + var encoded = (tuple.Values[d.GetValueIndex()] >> d.GetValueBitOffset()) & ValueBitMask; return Decode(encoded); } @@ -276,7 +279,7 @@ protected override long Encode(bool value) protected override bool Decode(long value) { - return value!=0; + return value != 0; } public BooleanFieldAccessor() @@ -296,7 +299,7 @@ protected override long Encode(float value) protected override float Decode(long value) { - var intValue = unchecked ((int) value); + var intValue = unchecked((int) value); unsafe { return *(float*) &intValue; } @@ -371,7 +374,7 @@ protected override long Encode(byte value) protected override byte Decode(long value) { - return unchecked ((byte) value); + return unchecked((byte) value); } public ByteFieldAccessor() @@ -389,7 +392,7 @@ protected override long Encode(sbyte value) protected override sbyte Decode(long value) { - return unchecked ((sbyte) value); + return unchecked((sbyte) value); } public SByteFieldAccessor() @@ -397,7 +400,7 @@ public SByteFieldAccessor() { } } - + internal sealed class ShortFieldAccessor : ValueFieldAccessor { protected override long Encode(short value) @@ -407,7 +410,7 @@ protected override long Encode(short value) protected override short Decode(long value) { - return unchecked ((short) value); + return unchecked((short) value); } public ShortFieldAccessor() @@ -415,7 +418,7 @@ public ShortFieldAccessor() { } } - + internal sealed class UShortFieldAccessor : ValueFieldAccessor { protected override long Encode(ushort value) @@ -425,7 +428,7 @@ protected override long Encode(ushort value) protected override ushort Decode(long value) { - return unchecked ((ushort) value); + return unchecked((ushort) value); } public UShortFieldAccessor() @@ -433,7 +436,7 @@ public UShortFieldAccessor() { } } - + internal sealed class IntFieldAccessor : ValueFieldAccessor { protected override long Encode(int value) @@ -443,7 +446,7 @@ protected override long Encode(int value) protected override int Decode(long value) { - return unchecked ((int) value); + return unchecked((int) value); } public IntFieldAccessor() @@ -451,7 +454,7 @@ public IntFieldAccessor() { } } - + internal sealed class UIntFieldAccessor : ValueFieldAccessor { protected override long Encode(uint value) @@ -461,7 +464,7 @@ protected override long Encode(uint value) protected override uint Decode(long value) { - return unchecked ((uint) value); + return unchecked((uint) value); } public UIntFieldAccessor() @@ -469,7 +472,7 @@ public UIntFieldAccessor() { } } - + internal sealed class LongFieldAccessor : ValueFieldAccessor { protected override long Encode(long value) @@ -487,7 +490,7 @@ public LongFieldAccessor() { } } - + internal sealed class ULongFieldAccessor : ValueFieldAccessor { protected override long Encode(ulong value) @@ -526,7 +529,7 @@ protected override void Encode(Guid value, long[] values, int offset) private static unsafe int GetSize() { - return sizeof (Guid); + return sizeof(Guid); } public GuidFieldAccessor() diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptiorExtensions.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptiorExtensions.cs new file mode 100644 index 0000000000..fe22b434a3 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptiorExtensions.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +namespace Xtensive.Tuples.Packed +{ + internal static class PackedFieldDescriptorExtensions + { + private const int OffsetBitCount = 6; + private const int OffsetMask = (1 << OffsetBitCount) - 1; + + public static bool IsObjectField(in this PackedFieldDescriptor descriptor) => descriptor.Accessor.Rank < 0; + public static int GetObjectIndex(in this PackedFieldDescriptor descriptor) => descriptor.DataPosition; + public static int GetValueIndex(in this PackedFieldDescriptor descriptor) => descriptor.DataPosition >> OffsetBitCount; + public static int GetValueBitOffset(in this PackedFieldDescriptor descriptor) => descriptor.DataPosition & OffsetMask; + public static int GetStateIndex(in this PackedFieldDescriptor descriptor) => descriptor.StatePosition >> OffsetBitCount; + public static int GetStateBitOffset(in this PackedFieldDescriptor descriptor) => descriptor.StatePosition & OffsetMask; + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptor.cs index 817a9008e8..96ab8939c0 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldDescriptor.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2012 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2012-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov // Created: 2012.12.29 @@ -11,23 +11,10 @@ namespace Xtensive.Tuples.Packed [Serializable] internal struct PackedFieldDescriptor { - private const int OffsetBitCount = 6; - private const int OffsetMask = (1 << OffsetBitCount) - 1; - - internal int DataPosition; - internal int StatePosition; + public int DataPosition; + public int StatePosition; [NonSerialized] public PackedFieldAccessor Accessor; - - public bool IsObjectField => Accessor.Rank < 0; - - public int ObjectIndex => DataPosition; - - public int ValueIndex => DataPosition >> OffsetBitCount; - public int ValueBitOffset => DataPosition & OffsetMask; - - public int StateIndex => StatePosition >> OffsetBitCount; - public int StateBitOffset => StatePosition & OffsetMask; } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Packed/PackedTuple.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedTuple.cs index a7b44cbd76..0838b03724 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedTuple.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2012 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2012-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov // Created: 2012.12.29 @@ -11,8 +11,6 @@ namespace Xtensive.Tuples.Packed [Serializable] internal sealed class PackedTuple : RegularTuple { - private static readonly object[] EmptyObjectArray = new object[0]; - public readonly TupleDescriptor PackedDescriptor; public readonly long[] Values; public readonly object[] Objects; @@ -34,28 +32,33 @@ public override Tuple CreateNew() public override bool Equals(Tuple other) { - var packedOther = other as PackedTuple; - if (packedOther==null) + if (!(other is PackedTuple packedOther)) { return base.Equals(other); + } - if (ReferenceEquals(packedOther, this)) + if (ReferenceEquals(packedOther, this)) { return true; - if (Descriptor!=packedOther.Descriptor) + } + if (Descriptor != packedOther.Descriptor) { return false; + } var fieldDescriptors = PackedDescriptor.FieldDescriptors; var count = Count; for (int i = 0; i < count; i++) { - ref var descriptor = ref fieldDescriptors[i]; - var thisState = GetFieldState(ref descriptor); - var otherState = packedOther.GetFieldState(ref descriptor); - if (thisState!=otherState) + ref readonly var descriptor = ref fieldDescriptors[i]; + var thisState = GetFieldState(descriptor); + var otherState = packedOther.GetFieldState(descriptor); + if (thisState != otherState) { return false; - if (thisState!=TupleFieldState.Available) + } + if (thisState != TupleFieldState.Available) { continue; + } var accessor = descriptor.Accessor; - if (!accessor.ValueEquals(this, ref descriptor, packedOther, ref descriptor)) + if (!accessor.ValueEquals(this, descriptor, packedOther, descriptor)) { return false; + } } return true; @@ -67,11 +70,11 @@ public override int GetHashCode() var fieldDescriptors = PackedDescriptor.FieldDescriptors; int result = 0; for (int i = 0; i < count; i++) { - ref var descriptor = ref fieldDescriptors[i]; + ref readonly var descriptor = ref fieldDescriptors[i]; var accessor = descriptor.Accessor; - var state = GetFieldState(ref fieldDescriptors[i]); - var fieldHash = state==TupleFieldState.Available - ? accessor.GetValueHashCode(this, ref descriptor) + var state = GetFieldState(descriptor); + var fieldHash = state == TupleFieldState.Available + ? accessor.GetValueHashCode(this, descriptor) : 0; result = HashCodeMultiplier * result ^ fieldHash; } @@ -80,45 +83,46 @@ public override int GetHashCode() public override TupleFieldState GetFieldState(int fieldIndex) { - return GetFieldState(ref PackedDescriptor.FieldDescriptors[fieldIndex]); + return GetFieldState(PackedDescriptor.FieldDescriptors[fieldIndex]); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - if (fieldState==TupleFieldState.Null) { + if (fieldState == TupleFieldState.Null) { throw new ArgumentOutOfRangeException(nameof(fieldState)); } - SetFieldState(ref PackedDescriptor.FieldDescriptors[fieldIndex], fieldState); + SetFieldState(PackedDescriptor.FieldDescriptors[fieldIndex], fieldState); } public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - ref var descriptor = ref PackedDescriptor.FieldDescriptors[fieldIndex]; - return descriptor.Accessor.GetUntypedValue(this, ref descriptor, out fieldState); + ref readonly var descriptor = ref PackedDescriptor.FieldDescriptors[fieldIndex]; + return descriptor.Accessor.GetUntypedValue(this, descriptor, out fieldState); } public override void SetValue(int fieldIndex, object fieldValue) { - ref var descriptor = ref PackedDescriptor.FieldDescriptors[fieldIndex]; - descriptor.Accessor.SetUntypedValue(this, ref descriptor, fieldValue); + ref readonly var descriptor = ref PackedDescriptor.FieldDescriptors[fieldIndex]; + descriptor.Accessor.SetUntypedValue(this, descriptor, fieldValue); } - public void SetFieldState(ref PackedFieldDescriptor d, TupleFieldState fieldState) + public void SetFieldState(in PackedFieldDescriptor d, TupleFieldState fieldState) { var bits = (long) fieldState; - var block = Values[d.StateIndex]; - Values[d.StateIndex] = (block & ~(3L << d.StateBitOffset)) | (bits << d.StateBitOffset); + int stateIndex = d.GetStateIndex(), stateBitOffset = d.GetStateBitOffset(); + var block = Values[stateIndex]; + Values[stateIndex] = (block & ~(3L << stateBitOffset)) | (bits << stateBitOffset); - if (fieldState!=TupleFieldState.Available && d.IsObjectField) { - Objects[d.ObjectIndex] = null; + if (fieldState != TupleFieldState.Available && d.IsObjectField()) { + Objects[d.GetObjectIndex()] = null; } } - public TupleFieldState GetFieldState(ref PackedFieldDescriptor d) + public TupleFieldState GetFieldState(in PackedFieldDescriptor d) { - var block = Values[d.StateIndex]; - return (TupleFieldState) ((block >> d.StateBitOffset) & 3); + int stateIndex = d.GetStateIndex(), stateBitOffset = d.GetStateBitOffset(); + return (TupleFieldState) ((Values[stateIndex] >> stateBitOffset) & 3); } public PackedTuple(TupleDescriptor descriptor) @@ -128,7 +132,7 @@ public PackedTuple(TupleDescriptor descriptor) Values = new long[PackedDescriptor.ValuesLength]; Objects = PackedDescriptor.ObjectsLength > 0 ? new object[PackedDescriptor.ObjectsLength] - : EmptyObjectArray; + : Array.Empty(); } private PackedTuple(PackedTuple origin) @@ -138,7 +142,7 @@ private PackedTuple(PackedTuple origin) Values = (long[]) origin.Values.Clone(); Objects = PackedDescriptor.ObjectsLength > 0 ? (object[]) origin.Objects.Clone() - : EmptyObjectArray; + : Array.Empty(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs index 677d5950bd..694fb6e74c 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs @@ -146,7 +146,7 @@ public static void ConfigureLen2(Type[] fieldTypes, ref PackedFieldDescriptor de valuesLength = 1; return; case 1: { - if (descriptor1.IsObjectField) { + if (descriptor1.IsObjectField()) { descriptor2.DataPosition = Val064BitCount; val1BitCount = descriptor2.Accessor.ValueBitCount; } @@ -212,7 +212,7 @@ public static void Configure(Type[] fieldTypes, PackedFieldDescriptor[] fieldDes for (var fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { ref var descriptor = ref fieldDescriptors[fieldIndex]; - if (descriptor.IsObjectField) { + if (descriptor.IsObjectField()) { continue; } diff --git a/Orm/Xtensive.Orm/Tuples/Tuple.cs b/Orm/Xtensive.Orm/Tuples/Tuple.cs index 03c17167ad..6db51a624b 100644 --- a/Orm/Xtensive.Orm/Tuples/Tuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Tuple.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2020 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Nick Svetlov @@ -111,14 +111,14 @@ public T GetValue(int fieldIndex, out TupleFieldState fieldState) var isNullable = null==default(T); // Is nullable value type or class if (this is PackedTuple packedTuple) { - ref var descriptor = ref packedTuple.PackedDescriptor.FieldDescriptors[fieldIndex]; - return descriptor.Accessor.GetValue(packedTuple, ref descriptor, isNullable, out fieldState); + ref readonly var descriptor = ref packedTuple.PackedDescriptor.FieldDescriptors[fieldIndex]; + return descriptor.Accessor.GetValue(packedTuple, descriptor, isNullable, out fieldState); } var mappedContainer = GetMappedContainer(fieldIndex, false); if (mappedContainer.First is PackedTuple mappedTuple) { - ref var descriptor = ref mappedTuple.PackedDescriptor.FieldDescriptors[mappedContainer.Second]; - return descriptor.Accessor.GetValue(mappedTuple, ref descriptor, isNullable, out fieldState); + ref readonly var descriptor = ref mappedTuple.PackedDescriptor.FieldDescriptors[mappedContainer.Second]; + return descriptor.Accessor.GetValue(mappedTuple, descriptor, isNullable, out fieldState); } var value = GetValue(fieldIndex, out fieldState); @@ -186,15 +186,15 @@ public void SetValue(int fieldIndex, T fieldValue) var isNullable = null==default(T); // Is nullable value type or class if (this is PackedTuple packedTuple) { - ref var descriptor = ref packedTuple.PackedDescriptor.FieldDescriptors[fieldIndex]; - descriptor.Accessor.SetValue(packedTuple, ref descriptor, isNullable, fieldValue); + ref readonly var descriptor = ref packedTuple.PackedDescriptor.FieldDescriptors[fieldIndex]; + descriptor.Accessor.SetValue(packedTuple, descriptor, isNullable, fieldValue); return; } var mappedContainer = GetMappedContainer(fieldIndex, true); if (mappedContainer.First is PackedTuple mappedTuple) { - ref var descriptor = ref mappedTuple.PackedDescriptor.FieldDescriptors[mappedContainer.Second]; - descriptor.Accessor.SetValue(mappedTuple, ref descriptor, isNullable, fieldValue); + ref readonly var descriptor = ref mappedTuple.PackedDescriptor.FieldDescriptors[mappedContainer.Second]; + descriptor.Accessor.SetValue(mappedTuple, descriptor, isNullable, fieldValue); return; } diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index fd0bdd6fb0..8eadc509d6 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2020 Xtensive LLC. +// Copyright (C) 2008-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alex Yakunin @@ -151,17 +151,16 @@ public static void CopyTo(this Tuple[] sources, Tuple target, Pair[] m /// Negative value in this map means "skip this element". internal static void CopyTo(this in FixedReadOnlyList3 sources, Tuple target, Pair[] map) { - var (packedSources, haveSlowSource) = sources.Count switch { - 1 => sources[0] is PackedTuple first ? (new FixedReadOnlyList3(first), false) : (default, true), - 2 => sources[0] is PackedTuple first && sources[1] is PackedTuple second ? (new FixedReadOnlyList3(first, second), false) : (default, true), - 3 => sources[0] is PackedTuple first && sources[1] is PackedTuple second && sources[2] is PackedTuple third - ? (new FixedReadOnlyList3(first, second, third), false) : (default, true), - _ => (default, false) - }; - - if (!haveSlowSource) { - var packedTarget = target as PackedTuple; - if (packedTarget!=null) { + if (target is PackedTuple packedTarget) { + var (packedSources, haveSlowSource) = sources.Count switch { + 1 => sources[0] is PackedTuple first ? (new FixedReadOnlyList3(first), false) : (default, true), + 2 => sources[0] is PackedTuple first && sources[1] is PackedTuple second ? (new FixedReadOnlyList3(first, second), false) : (default, true), + 3 => sources[0] is PackedTuple first && sources[1] is PackedTuple second && sources[2] is PackedTuple third + ? (new FixedReadOnlyList3(first, second, third), false) : (default, true), + _ => (default, false) + }; + + if (!haveSlowSource) { Copy3TuplesWithMappingFast(packedSources, packedTarget, map); return; } @@ -418,10 +417,10 @@ private static void CopyValue(Tuple source, int sourceIndex, Tuple target, int t private static void CopyPackedValue(PackedTuple source, int sourceIndex, PackedTuple target, int targetIndex) { - ref var sourceDescriptor = ref source.PackedDescriptor.FieldDescriptors[sourceIndex]; - ref var targetDescriptor = ref target.PackedDescriptor.FieldDescriptors[targetIndex]; + ref readonly var sourceDescriptor = ref source.PackedDescriptor.FieldDescriptors[sourceIndex]; + ref readonly var targetDescriptor = ref target.PackedDescriptor.FieldDescriptors[targetIndex]; - var fieldState = source.GetFieldState(ref sourceDescriptor); + var fieldState = source.GetFieldState(sourceDescriptor); if (!fieldState.IsAvailable()) { return; } @@ -440,7 +439,7 @@ private static void CopyPackedValue(PackedTuple source, int sourceIndex, PackedT } target.SetFieldState(targetIndex, TupleFieldState.Available); - accessor.CopyValue(source, ref sourceDescriptor, target, ref targetDescriptor); + accessor.CopyValue(source, sourceDescriptor, target, targetDescriptor); } private static void PartiallyCopyTupleSlow(Tuple source, Tuple target, int sourceStartIndex, int targetStartIndex, int length) From 521307533c6269932475e38946ee7f1f35358f60 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 18:39:47 -0800 Subject: [PATCH 13/64] Simplify CombineTransform implementation --- .../Tuples/Transform/MergeTransformTest.cs | 64 +--------------- .../Tuples/Transform/CombineTransform.cs | 61 +++++---------- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 75 ++++++++++--------- 3 files changed, 60 insertions(+), 140 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs index 96e33fc3ab..3b036ac85a 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs @@ -1,17 +1,13 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2008-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Yakunin // Created: 2008.06.05 using System; using NUnit.Framework; using Xtensive.Comparison; -using Xtensive.Core; -using Xtensive.Tuples; -using Xtensive.Orm.Tests; using Xtensive.Tuples.Transform; -using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Tests.Core.Tuples.Transform { @@ -57,24 +53,6 @@ public void BaseTest() AssertEx.Throws(delegate { wtro.SetValue(2, 0); }); - - CombineTransform mt3 = new CombineTransform(false, t1.Descriptor, t1.Descriptor, t1.Descriptor); - Xtensive.Tuples.Tuple wt3 = mt3.Apply(TupleTransformType.TransformedTuple, t1, t1, t1); - TestLog.Info("Wrapper: {0}", wt3); - Xtensive.Tuples.Tuple ct3 = mt3.Apply(TupleTransformType.Tuple, t1, t1, t1); - TestLog.Info("Copy: {0}", ct3); - t1.SetValue(0,0); - Assert.AreEqual(wt3.GetValue(4), t1.GetValue(0)); - t1.SetValue(0,1); - - CombineTransform mt4 = new CombineTransform(false, t1.Descriptor, t1.Descriptor, t1.Descriptor, t1.Descriptor); - Xtensive.Tuples.Tuple wt4 = mt4.Apply(TupleTransformType.TransformedTuple, t1, t1, t1, t1); - TestLog.Info("Wrapper: {0}", wt4); - Xtensive.Tuples.Tuple ct4 = mt4.Apply(TupleTransformType.Tuple, t1, t1, t1, t1); - TestLog.Info("Copy: {0}", ct4); - t1.SetValue(0,0); - Assert.AreEqual(wt4.GetValue(6), t1.GetValue(0)); - t1.SetValue(0,1); } [Test] @@ -110,41 +88,5 @@ public void PerformanceTest1() for (int i = 0; i comparer = AdvancedComparerStruct.Default; - Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1); - CombineTransform mt = new CombineTransform(false, t.Descriptor, t.Descriptor, t.Descriptor, t.Descriptor); - SegmentTransform st = new SegmentTransform(false, mt.Descriptor, new Segment(1,2)); - Xtensive.Tuples.Tuple wt1 = st.Apply(TupleTransformType.TransformedTuple, mt.Apply(TupleTransformType.TransformedTuple, t, t, t, t)); - Xtensive.Tuples.Tuple wt2 = st.Apply(TupleTransformType.TransformedTuple, mt.Apply(TupleTransformType.TransformedTuple, t, t, t, t)); - Xtensive.Tuples.Tuple ct1 = st.Apply(TupleTransformType.Tuple, mt.Apply(TupleTransformType.Tuple, t, t, t, t)); - Xtensive.Tuples.Tuple ct2 = st.Apply(TupleTransformType.Tuple, mt.Apply(TupleTransformType.Tuple, t, t, t, t)); - int count = IterationCount; - - comparer.Equals(ct1, ct2); - comparer.Equals(ct1, wt1); - comparer.Equals(wt1, wt2); - - TestHelper.CollectGarbage(); - using (new Measurement("O&O", MeasurementOptions.Log, count)) - for (int i = 0; i @@ -20,51 +16,29 @@ namespace Xtensive.Tuples.Transform [Serializable] public sealed class CombineTransform : MapTransform { - private TupleDescriptor[] sources; - - /// - /// Gets tuple descriptors this transform merges. - /// - public IReadOnlyList Sources - { - [DebuggerStepThrough] - get => Array.AsReadOnly(sources); - } - - /// - public new Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) - { - return base.Apply(transformType, source1, source2); - } - - /// - public new Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) - { - return base.Apply(transformType, source1, source2, source3); - } + private readonly (TupleDescriptor first, TupleDescriptor second) sources; /// public override string ToString() { - string description = $"{sources.ToDelimitedString(" + ")}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), + string description = $"{sources.first} + {sources.second}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + return string.Format(Strings.TupleTransformFormat, + GetType().GetShortName(), description); } - // Constructors - - private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor[] sources, out Pair[] map) + + private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, TupleDescriptor second) sources, out Pair[] map) { - int totalLength = sources.Sum(s => s.Count); + int totalLength = sources.first.Count + sources.second.Count; var types = new Type[totalLength]; map = new Pair[totalLength]; int index = 0; - for (int i = 0; i(i, j); } @@ -76,11 +50,12 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor[] sources, /// Initializes a new instance of this type. /// /// property value. - /// Source tuple descriptors. - public CombineTransform(bool isReadOnly, params TupleDescriptor[] sources) - : base(isReadOnly, CreateDescriptorAndMap(sources, out var map), map) + /// First tuple descriptor to combine. + /// Second tuple descriptor to combine. + public CombineTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) + : base(isReadOnly, CreateDescriptorAndMap((first, second), out var map), map) { - this.sources = sources; + this.sources = (first, second); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index 8eadc509d6..c6054aaaaf 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -35,13 +35,12 @@ public static class TupleExtensions /// The number of elements to copy. public static void CopyTo(this Tuple source, Tuple target, int startIndex, int targetStartIndex, int length) { - var packedSource = source as PackedTuple; - var packedTarget = target as PackedTuple; - - if (packedSource!=null && packedTarget!=null) + if (source is PackedTuple packedSource && target is PackedTuple packedTarget) { PartiallyCopyTupleFast(packedSource, packedTarget, startIndex, targetStartIndex, length); - else + } + else { PartiallyCopyTupleSlow(source, target, startIndex, targetStartIndex, length); + } } /// @@ -97,13 +96,12 @@ public static void CopyTo(this Tuple source, Tuple target) /// Negative value in this map means "skip this element". public static void CopyTo(this Tuple source, Tuple target, IReadOnlyList map) { - var packedSource = source as PackedTuple; - var packedTarget = target as PackedTuple; - - if (packedSource!=null && packedTarget!=null) + if (source is PackedTuple packedSource && target is PackedTuple packedTarget) { CopyTupleWithMappingFast(packedSource, packedTarget, map); - else + } + else { CopyTupleWithMappingSlow(source, target, map); + } } /// @@ -117,21 +115,21 @@ public static void CopyTo(this Tuple source, Tuple target, IReadOnlyList ma /// Negative value in this map means "skip this element". public static void CopyTo(this Tuple[] sources, Tuple target, Pair[] map) { - var haveSlowSource = false; - var packedSources = new PackedTuple[sources.Length]; - - for (int i = 0; i < sources.Length; i++) { - var packedSource = sources[i] as PackedTuple; - if (packedSource==null) { - haveSlowSource = true; - break; + if (target is PackedTuple packedTarget) { + var haveSlowSource = false; + var packedSources = new PackedTuple[sources.Length]; + + for (int i = 0; i < sources.Length; i++) { + if (sources[i] is PackedTuple packedSource) { + packedSources[i] = packedSource; + } + else { + haveSlowSource = true; + break; + } } - packedSources[i] = packedSource; - } - if (!haveSlowSource) { - var packedTarget = target as PackedTuple; - if (packedTarget!=null) { + if (!haveSlowSource) { CopyTupleArrayWithMappingFast(packedSources, packedTarget, map); return; } @@ -149,20 +147,25 @@ public static void CopyTo(this Tuple[] sources, Tuple target, Pair[] m /// Tuple that receives the data. /// Target-to-source field index map. /// Negative value in this map means "skip this element". - internal static void CopyTo(this in FixedReadOnlyList3 sources, Tuple target, Pair[] map) + internal static void CopyTo(in this FixedReadOnlyList3 sources, Tuple target, Pair[] map) { if (target is PackedTuple packedTarget) { - var (packedSources, haveSlowSource) = sources.Count switch { - 1 => sources[0] is PackedTuple first ? (new FixedReadOnlyList3(first), false) : (default, true), - 2 => sources[0] is PackedTuple first && sources[1] is PackedTuple second ? (new FixedReadOnlyList3(first, second), false) : (default, true), - 3 => sources[0] is PackedTuple first && sources[1] is PackedTuple second && sources[2] is PackedTuple third - ? (new FixedReadOnlyList3(first, second, third), false) : (default, true), - _ => (default, false) - }; - - if (!haveSlowSource) { - Copy3TuplesWithMappingFast(packedSources, packedTarget, map); - return; + var count = sources.Count; + if (count > 0 && sources[0] is PackedTuple packedFirst) { + if (count==1) { + Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst), packedTarget, map); + return; + } + else if (sources[1] is PackedTuple packedSecond) { + if (count==2) { + Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst, packedSecond), packedTarget, map); + return; + } + else if (sources[2] is PackedTuple packedThird) { + Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst, packedSecond, packedThird), packedTarget, map); + return; + } + } } } @@ -181,7 +184,7 @@ internal static void CopyTo(this in FixedReadOnlyList3 sources, Tuple tar /// public static Tuple Combine(this Tuple left, Tuple right) { - var transform = new CombineTransform(false, new[] {left.Descriptor, right.Descriptor}); + var transform = new CombineTransform(false, left.Descriptor, right.Descriptor); return transform.Apply(TupleTransformType.TransformedTuple, left, right); } From fed8a0be81dc6e7a98b3105769a7bdd30b7cf6a0 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 21:23:14 -0800 Subject: [PATCH 14/64] Convert TupleTransformBase to an intrface --- .../Tuples/Transform/CombineTransform.cs | 2 +- .../Tuples/Transform/ITupleTransform.cs | 25 ++++++++++++ .../Tuples/Transform/MapTransform.cs | 33 +++++++++++---- .../Tuples/Transform/SegmentTransform.cs | 2 +- .../TransformedTuple{TTupleTransform}.cs | 2 +- .../Tuples/Transform/TupleTransformBase.cs | 40 ------------------- 6 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs index 8a6a8e4fee..e5537bb617 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs @@ -49,7 +49,7 @@ private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, /// /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// First tuple descriptor to combine. /// Second tuple descriptor to combine. public CombineTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs new file mode 100644 index 0000000000..1e3f073322 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2008-2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. +// Created by: Alexey Kochetov +// Created: 2008.04.30 + +namespace Xtensive.Tuples.Transform +{ + /// + /// Interface implemented by any of the tuple transforms. + /// + public interface ITupleTransform + { + /// + /// Gets describing the tuples + /// this transform may produce. + /// + TupleDescriptor Descriptor { get; } + + /// + /// Indicates whether transform always produces read-only tuples or not. + /// + bool IsReadOnly { get; } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index fa31a058e6..e85acd1fb3 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -20,12 +20,23 @@ namespace Xtensive.Tuples.Transform /// Maps fields of destination tuple to fields of a set of source tuples. /// [Serializable] - public class MapTransform : TupleTransformBase + public class MapTransform : ITupleTransform { private int sourceCount; internal IReadOnlyList singleSourceMap; internal Pair[] map; + /// + /// Gets describing the tuples + /// this transform may produce. + /// + public TupleDescriptor Descriptor { get; } + + /// + /// Indicates whether transform always produces read-only tuples or not. + /// > + public bool IsReadOnly { get; } + /// /// Means that no mapping is available for the specified field index. /// @@ -199,13 +210,16 @@ public override string ToString() /// /// Initializes a new instance of this type. /// - /// property value. - /// Initial property value. + /// property value. + /// Initial property value. /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[] map) - : base(descriptor, isReadOnly) { + ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); + + IsReadOnly = isReadOnly; + Descriptor = descriptor; var newFirstSourceMap = new int[map.Length]; var index = 0; var newSourceCount = -1; @@ -225,13 +239,16 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[ /// /// Initializes a new instance of this type. /// - /// property value. - /// Initial property value. + /// property value. + /// Initial property value. /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList singleSourceMap) - : base(descriptor, isReadOnly) { - ArgumentValidator.EnsureArgumentNotNull(singleSourceMap, nameof(singleSourceMap)); + ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); + ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); + + IsReadOnly = isReadOnly; + Descriptor = descriptor; var newMap = new Pair[Descriptor.Count]; var index = 0; for (; index < newMap.Length && index < singleSourceMap.Count; index++) { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 7e46e8d46a..13052c0837 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -61,7 +61,7 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDesc /// /// Initializes a new instance of this type. /// - /// property value. + /// property value. /// Source tuple descriptor. /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs index e2ebbae53c..8b14fc2eef 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs @@ -15,7 +15,7 @@ namespace Xtensive.Tuples.Transform /// [Serializable] public abstract class TransformedTuple : Tuple, ITransformedTuple - where TTupleTransform : TupleTransformBase + where TTupleTransform : ITupleTransform { /// /// Gets or sets the transform used to produce this instance. diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs deleted file mode 100644 index c02c996021..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.04.30 - -using System; -using Xtensive.Core; -using Xtensive.Reflection; - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for any tuple transform. - /// - [Serializable] - public abstract class TupleTransformBase - { - /// - /// Gets describing the tuples - /// this transform may produce. - /// - public TupleDescriptor Descriptor { get; } - - /// - /// Indicates whether transform always produces read-only tuples or not. - /// - public bool IsReadOnly { get; } - - /// - public override string ToString() => $"[{GetType().GetShortName()}, {(IsReadOnly ? "readOnly" : string.Empty)}]"; - - protected TupleTransformBase(TupleDescriptor descriptor, bool isReadOnly) - { - ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); - Descriptor = descriptor; - IsReadOnly = isReadOnly; - } - } -} \ No newline at end of file From 9b0ed841c39b5bc43f449ab95843355dc90fc18b Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 21:42:56 -0800 Subject: [PATCH 15/64] Use composition instead of inheritance for the CombineTransform --- .../Tuples/Transform/CombineTransform.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs index e5537bb617..5552cf838e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs @@ -14,10 +14,29 @@ namespace Xtensive.Tuples.Transform /// This class is used for source s combining. /// [Serializable] - public sealed class CombineTransform : MapTransform + public sealed class CombineTransform : ITupleTransform { + private MapTransform mapTransform; private readonly (TupleDescriptor first, TupleDescriptor second) sources; + /// + public TupleDescriptor Descriptor => mapTransform.Descriptor; + + /// + public bool IsReadOnly => mapTransform.IsReadOnly; + + /// + /// Applies the transformation. + /// + /// The type of transformation to perform. + /// First transformation source. + /// Second transformation source. + /// Transformation result - + /// either or descendant, + /// dependently on specified . + public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) + => mapTransform.Apply(transformType, source1, source2); + /// public override string ToString() { @@ -53,8 +72,8 @@ private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, /// First tuple descriptor to combine. /// Second tuple descriptor to combine. public CombineTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) - : base(isReadOnly, CreateDescriptorAndMap((first, second), out var map), map) { + mapTransform = new MapTransform(isReadOnly, CreateDescriptorAndMap((first, second), out var map), map); this.sources = (first, second); } } From 41078751e282f08de8c77941be907bd84c134fa8 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 22:00:40 -0800 Subject: [PATCH 16/64] Use composition instead of inheritance for the SegmentTransform --- .../Tuples/Transform/SegmentTransform.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 13052c0837..7d9a8a1f99 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -16,8 +16,16 @@ namespace Xtensive.Tuples.Transform /// /// Extracts specified from the . /// - public sealed class SegmentTransform : MapTransform + public sealed class SegmentTransform : ITupleTransform { + private MapTransform mapTransform; + + /// + public TupleDescriptor Descriptor => mapTransform.Descriptor; + + /// + public bool IsReadOnly => mapTransform.IsReadOnly; + private Segment segment; /// @@ -30,9 +38,9 @@ public Segment Segment } /// - public new Tuple Apply(TupleTransformType transformType, Tuple source) + public Tuple Apply(TupleTransformType transformType, Tuple source) { - return base.Apply(transformType, source); + return mapTransform.Apply(transformType, source); } /// @@ -65,8 +73,8 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDesc /// Source tuple descriptor. /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) - : base(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map) { + mapTransform = new MapTransform(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map); this.segment = segment; } } From 4a674389af0fd2383b6815fab0d5910c9e992705 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Thu, 23 Dec 2021 22:02:07 -0800 Subject: [PATCH 17/64] Remove unused methods from the MapTransform class --- .../Tuples/Transform/MapTransform.cs | 76 ------------------- 1 file changed, 76 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index e85acd1fb3..f055d57f5a 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -57,46 +57,6 @@ public class MapTransform : ITupleTransform /// public IReadOnlyList> Map => map; - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// Transformation sources. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) - { - ArgumentValidator.EnsureArgumentNotNull(sources, nameof(sources)); - if (sourceCount > sources.Length) { - throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); - } - switch (sourceCount) { - case 1: - return Apply(transformType, sources[0]); - case 2: - return Apply(transformType, sources[0], sources[1]); - case 3: - return Apply(transformType, sources[0], sources[1], sources[2]); - default: - switch (transformType) { - case TupleTransformType.Auto: - foreach (Tuple tuple in sources) - if (tuple is ITransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple(this, sources); - case TupleTransformType.Tuple: - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); - } - } - } - /// /// Applies the transformation. /// @@ -159,42 +119,6 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source } } - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// First transformation source. - /// Second transformation source. - /// Third transformation source. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2, Tuple source3) - { - if (sourceCount > 3) { - throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); - } - switch (transformType) { - case TupleTransformType.Auto: - if (source1 is ITransformedTuple) - goto case TupleTransformType.Tuple; - if (source2 is ITransformedTuple) - goto case TupleTransformType.Tuple; - if (source3 is ITransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple3(this, source1, source2, source3); - case TupleTransformType.Tuple: - var sources = new FixedReadOnlyList3(source1, source2, source3); - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); - } - } - /// public override string ToString() { From db7efb0cd953ba5a9cbc152d1352d07bd3ac59b1 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 27 Dec 2021 17:15:35 -0800 Subject: [PATCH 18/64] Introduce SingleSourceMapTransform transformation --- .../Tuples/Transform/MapTransformTest.cs | 6 +- Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs | 6 +- .../Materialization/MaterializationContext.cs | 4 +- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 4 +- .../Providers/Compilable/AggregateProvider.cs | 4 +- .../Providers/Compilable/CalculateProvider.cs | 4 +- .../Providers/Compilable/IncludeProvider.cs | 4 +- .../Providers/Compilable/OrderProviderBase.cs | 4 +- .../Providers/Compilable/RowNumberProvider.cs | 4 +- .../Transformation/RedundantColumnRemover.cs | 4 +- .../Transform/Internals/MapTransformTuple.cs | 80 -------------- .../Transform/Internals/MapTransformTuple1.cs | 6 +- .../Tuples/Transform/MapTransform.cs | 70 +----------- .../Tuples/Transform/SegmentTransform.cs | 16 ++- .../Transform/SingleSourceMapTransform.cs | 101 ++++++++++++++++++ Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 11 +- 16 files changed, 139 insertions(+), 189 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs create mode 100644 Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs index f2be75612e..ffee34da37 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs @@ -16,9 +16,9 @@ public class MapTransformTest { public void MainTest() { - Xtensive.Tuples.Tuple source = Tuple.Create(1); - MapTransform transform = new MapTransform(true, TupleDescriptor.Create(), new[] {-1, 0}); - Xtensive.Tuples.Tuple result = transform.Apply(TupleTransformType.TransformedTuple, source); + var source = Tuple.Create(1); + var transform = new SingleSourceMapTransform(true, TupleDescriptor.Create(), new[] {-1, 0}); + var result = transform.Apply(TupleTransformType.TransformedTuple, source); Assert.AreEqual(TupleFieldState.Default, result.GetFieldState(0)); Assert.AreEqual(TupleFieldState.Available, result.GetFieldState(1)); Assert.AreEqual(TupleFieldState.Default, result.GetFieldState(2)); diff --git a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs index 9848fbaec1..bde10db25c 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs @@ -13,14 +13,14 @@ namespace Xtensive.Orm.Internals internal readonly struct TypeMapping { public readonly TypeInfo Type; - public readonly MapTransform KeyTransform; + public readonly SingleSourceMapTransform KeyTransform; + public readonly SingleSourceMapTransform Transform; public readonly IReadOnlyList KeyIndexes; - public readonly MapTransform Transform; // Constructors - public TypeMapping(TypeInfo type, MapTransform keyTransform, MapTransform transform, IReadOnlyList keyIndexes) + public TypeMapping(TypeInfo type, SingleSourceMapTransform keyTransform, SingleSourceMapTransform transform, IReadOnlyList keyIndexes) { Type = type; KeyTransform = keyTransform; diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs index 0909a1c341..a79b73c7c6 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs @@ -93,8 +93,8 @@ public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int ArraySegment allIndexes = MaterializationHelper.CreateSingleSourceMap(descriptor.Count, typeColumnMap); ArraySegment keyIndexes = allIndexes.Slice(0, keyInfo.TupleDescriptor.Count); - var transform = new MapTransform(true, descriptor, allIndexes); - var keyTransform = new MapTransform(true, keyInfo.TupleDescriptor, keyIndexes); + var transform = new SingleSourceMapTransform(true, descriptor, allIndexes); + var keyTransform = new SingleSourceMapTransform(true, keyInfo.TupleDescriptor, keyIndexes); result = new TypeMapping(type, keyTransform, transform, keyIndexes); diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index a34e95f9ae..f02b100f16 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -342,7 +342,7 @@ public object TypeDiscriminatorValue { /// /// Gets the version tuple extractor. /// - public MapTransform VersionExtractor { get; private set; } + public SingleSourceMapTransform VersionExtractor { get; private set; } /// /// Gets a value indicating whether this instance has version fields. @@ -818,7 +818,7 @@ private void BuildVersionExtractor() var types = versionColumns.Select(c => c.ValueType).ToArray(versionColumnsCount); var map = versionColumns.Select(c => c.Field.MappingInfo.Offset).ToArray(versionColumnsCount); var versionTupleDescriptor = TupleDescriptor.Create(types); - VersionExtractor = new MapTransform(true, versionTupleDescriptor, map); + VersionExtractor = new SingleSourceMapTransform(true, versionTupleDescriptor, map); } private IDictionary, FieldInfo> BuildStructureFieldMapping() diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index e4f26b7c1f..dfaf4be6d9 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -40,7 +40,7 @@ public sealed class AggregateProvider : UnaryProvider /// /// Gets header resize transform. /// - public MapTransform Transform { get; private set; } + public SingleSourceMapTransform Transform { get; private set; } /// protected override RecordSetHeader BuildHeader() @@ -81,7 +81,7 @@ protected override void Initialize() columnIndexes[i] = index; i++; } - Transform = new MapTransform(false, TupleDescriptor.Create(fieldTypes), columnIndexes); + Transform = new SingleSourceMapTransform(false, TupleDescriptor.Create(fieldTypes), columnIndexes); } /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index 8a8914d45c..d55c29db8f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -32,7 +32,7 @@ public class CalculateProvider : UnaryProvider, /// /// Gets header resize transform. /// - public MapTransform ResizeTransform { get; private set; } + public SingleSourceMapTransform ResizeTransform { get; private set; } /// @@ -54,7 +54,7 @@ protected override void Initialize() var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) columnIndexes[i] = (i < Source.Header.Length) ? i : MapTransform.NoMapping; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); + ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 44d508b7b9..19155f1e36 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -53,7 +53,7 @@ public sealed class IncludeProvider: UnaryProvider, /// public Expression>> FilterDataSource { get; private set; } - public MapTransform FilteredColumnsExtractionTransform { get; private set; } + public SingleSourceMapTransform FilteredColumnsExtractionTransform { get; private set; } public CombineTransform ResultTransform { get; private set; } @@ -68,7 +68,7 @@ protected override RecordSetHeader BuildHeader() fieldTypes[index] = newHeader.Columns[FilteredColumns[index]].Type; } var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - FilteredColumnsExtractionTransform = new MapTransform(true, tupleDescriptor, FilteredColumns); + FilteredColumnsExtractionTransform = new SingleSourceMapTransform(true, tupleDescriptor, FilteredColumns); ResultTransform = new CombineTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); return newHeader; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs index b47123b259..603f2a7f08 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs @@ -33,7 +33,7 @@ public abstract class OrderProviderBase : UnaryProvider /// /// Gets the key extractor transform. /// - public MapTransform OrderKeyExtractorTransform { get; private set; } + public SingleSourceMapTransform OrderKeyExtractorTransform { get; private set; } /// /// Extracts the key part from using . @@ -82,7 +82,7 @@ protected override void Initialize() map[i] = p.Key; } var orderKeyDescriptor = TupleDescriptor.Create(fieldTypes); - OrderKeyExtractorTransform = new MapTransform(true, orderKeyDescriptor, map); + OrderKeyExtractorTransform = new SingleSourceMapTransform(true, orderKeyDescriptor, map); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 257440ae8e..39229ec221 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -25,7 +25,7 @@ public sealed class RowNumberProvider : UnaryProvider /// /// Gets header resize transform. /// - public MapTransform ResizeTransform { get; private set; } + public SingleSourceMapTransform ResizeTransform { get; private set; } /// protected override void Initialize() @@ -34,7 +34,7 @@ protected override void Initialize() var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) columnIndexes[i] = (i < Source.Header.Length) ? i : MapTransform.NoMapping; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); + ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); } /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs index 9ef551e717..a232228af6 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs @@ -41,13 +41,13 @@ protected override Provider VisitRaw(RawProvider provider) var mapping = mappings[provider]; if (mapping.SequenceEqual(Enumerable.Range(0, provider.Header.Length))) return provider; - var mappingTransform = new MapTransform(true, provider.Header.TupleDescriptor, mapping.ToArray()); + var mappingTransform = new SingleSourceMapTransform(true, provider.Header.TupleDescriptor, mapping); var newExpression = RemapRawProviderSource(provider.Source, mappingTransform); return new RawProvider(provider.Header.Select(mapping), newExpression); } private static Expression>> RemapRawProviderSource( - Expression>> source, MapTransform mappingTransform) + Expression>> source, SingleSourceMapTransform mappingTransform) { Func selector = tuple => mappingTransform.Apply(TupleTransformType.Auto, tuple); var newExpression = Expression.Call(SelectMethodInfo, source.Body, Expression.Constant(selector)); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs deleted file mode 100644 index a3df3343f0..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.05.07 - -using System; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform.Internals -{ - /// - /// A result tuple mapping arbitrary count of source tuples to a single one (this). - /// - [Serializable] - internal sealed class MapTransformTuple : TransformedTuple - { - private readonly Tuple[] tuples; - - #region GetFieldState, GetValue, SetValue methods - - /// - public override TupleFieldState GetFieldState(int fieldIndex) - { - var indexes = TupleTransform.map[fieldIndex]; - return tuples[indexes.First].GetFieldState(indexes.Second); - } - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - var indexes = TupleTransform.map[fieldIndex]; - tuples[indexes.First].SetFieldState(indexes.Second, fieldState); - } - - /// - public override object GetValue(int fieldIndex, out TupleFieldState fieldState) - { - var indexes = TupleTransform.map[fieldIndex]; - return tuples[indexes.First].GetValue(indexes.Second, out fieldState); - } - - /// - public override void SetValue(int fieldIndex, object fieldValue) - { - if (TupleTransform.IsReadOnly) { - throw Exceptions.ObjectIsReadOnly(null); - } - var indexes = TupleTransform.map[fieldIndex]; - tuples[indexes.First].SetValue(indexes.Second, fieldValue); - } - - #endregion - - protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) - { - if (isWriting && TupleTransform.IsReadOnly) { - throw Exceptions.ObjectIsReadOnly(null); - } - var map = TupleTransform.map[fieldIndex]; - return tuples[map.First].GetMappedContainer(map.Second, isWriting); - } - - - // Constructors - - /// - /// Initializes new instance of this type. - /// - /// The transform. - /// Source tuples. - public MapTransformTuple(MapTransform transform, Tuple[] sources) - : base(transform) - { - ArgumentValidator.EnsureArgumentNotNull(sources, "tuples"); - // Other checks are omitted: this transform should be fast, so delayed errors are ok - this.tuples = sources; - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index 427d0c6025..d39653c9f8 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - internal sealed class MapTransformTuple1 : TransformedTuple + internal sealed class MapTransformTuple1 : TransformedTuple { private readonly Tuple source; private Tuple defaultResult; @@ -65,7 +65,7 @@ public override void SetValue(int fieldIndex, object fieldValue) private int GetMappedFieldIndex(int fieldIndex) { - var mappedIndex = TupleTransform.singleSourceMap[fieldIndex]; + var mappedIndex = TupleTransform.Map[fieldIndex]; return mappedIndex < 0 ? MapTransform.NoMapping : mappedIndex; } @@ -86,7 +86,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// /// The transform. /// Source tuple. - public MapTransformTuple1(MapTransform transform, Tuple source) + public MapTransformTuple1(SingleSourceMapTransform transform, Tuple source) : base(transform) { this.source = source; diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index f055d57f5a..71ceba9096 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -23,7 +23,6 @@ namespace Xtensive.Tuples.Transform public class MapTransform : ITupleTransform { private int sourceCount; - internal IReadOnlyList singleSourceMap; internal Pair[] map; /// @@ -42,50 +41,11 @@ public class MapTransform : ITupleTransform /// public const int NoMapping = int.MinValue; - /// - /// Gets the count of source this transform maps to the target one. - /// - public int SourceCount => sourceCount; - - /// - /// Gets or sets destination-to-source field map for the first source only. - /// - public IReadOnlyList SingleSourceMap => singleSourceMap; - /// /// Gets or sets destination-to-source field map. /// public IReadOnlyList> Map => map; - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// Transformation source. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, Tuple source) - { - if (sourceCount > 1) { - throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); - } - switch (transformType) { - case TupleTransformType.Auto: - if (source is ITransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple1(this, source); - case TupleTransformType.Tuple: - Tuple result = Tuple.Create(Descriptor); - source.CopyTo(result, singleSourceMap); - return result; - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); - } - } - /// /// Applies the transformation. /// @@ -122,7 +82,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source /// public override string ToString() { - string description = $"{SourceCount}: {(SourceCount == 1 ? singleSourceMap.ToCommaDelimitedString() : map.ToCommaDelimitedString())}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + string description = $"{sourceCount}: {map.ToCommaDelimitedString()}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; return string.Format(Strings.TupleTransformFormat, GetType().GetShortName(), description); @@ -156,35 +116,7 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[ newSourceCount++; this.map = map; - singleSourceMap = newSourceCount == 1 ? newFirstSourceMap : null; sourceCount = newSourceCount; } - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Initial property value. - /// property value. - public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList singleSourceMap) - { - ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); - ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); - - IsReadOnly = isReadOnly; - Descriptor = descriptor; - var newMap = new Pair[Descriptor.Count]; - var index = 0; - for (; index < newMap.Length && index < singleSourceMap.Count; index++) { - newMap[index] = new Pair(0, singleSourceMap[index]); - } - while (index < newMap.Length) { - newMap[index++] = new Pair(0, NoMapping); - } - - map = newMap; - this.singleSourceMap = singleSourceMap; - sourceCount = 1; - } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 7d9a8a1f99..4970854aae 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -18,7 +18,8 @@ namespace Xtensive.Tuples.Transform /// public sealed class SegmentTransform : ITupleTransform { - private MapTransform mapTransform; + private readonly SingleSourceMapTransform mapTransform; + private readonly Segment segment; /// public TupleDescriptor Descriptor => mapTransform.Descriptor; @@ -26,8 +27,6 @@ public sealed class SegmentTransform : ITupleTransform /// public bool IsReadOnly => mapTransform.IsReadOnly; - private Segment segment; - /// /// Gets the segment this transform extracts. /// @@ -37,7 +36,14 @@ public Segment Segment get { return segment; } } - /// + /// + /// Applies the transformation. + /// + /// The type of transformation to perform. + /// Transformation source. + /// Transformation result - + /// either or descendant, + /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { return mapTransform.Apply(transformType, source); @@ -74,7 +80,7 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDesc /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) { - mapTransform = new MapTransform(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map); + mapTransform = new SingleSourceMapTransform(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map); this.segment = segment; } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs new file mode 100644 index 0000000000..5438a8fe39 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs @@ -0,0 +1,101 @@ +// Copyright (C) 2003-2010 Xtensive LLC. +// All rights reserved. +// For conditions of distribution and use, see license. +// Created by: Alexey Kochetov +// Created: 2008.05.07 + +using System; +using System.Collections.Generic; +using Xtensive.Core; +using Xtensive.Reflection; + + +using Xtensive.Tuples.Transform.Internals; + +namespace Xtensive.Tuples.Transform +{ + /// + /// Maps fields of a destination tuple to the specified fields of of the source tuple. + /// + [Serializable] + public class SingleSourceMapTransform : ITupleTransform + { + private IReadOnlyList map; + + /// + /// Gets describing the tuples + /// this transform may produce. + /// + public TupleDescriptor Descriptor { get; } + + /// + /// Indicates whether transform always produces read-only tuples or not. + /// > + public bool IsReadOnly { get; } + + /// + /// Means that no mapping is available for the specified field index. + /// + public const int NoMapping = int.MinValue; + + /// + /// Gets or sets destination-to-source field map for the first source only. + /// + public IReadOnlyList Map => map; + + /// + /// Applies the transformation. + /// + /// The type of transformation to perform. + /// Transformation source. + /// Transformation result - + /// either or descendant, + /// dependently on specified . + public Tuple Apply(TupleTransformType transformType, Tuple source) + { + switch (transformType) { + case TupleTransformType.Auto: + if (source is ITransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new MapTransformTuple1(this, source); + case TupleTransformType.Tuple: + Tuple result = Tuple.Create(Descriptor); + source.CopyTo(result, map); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); + } + } + + /// + public override string ToString() + { + string description = $"1: {map.ToCommaDelimitedString()}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + return string.Format(Strings.TupleTransformFormat, + GetType().GetShortName(), + description); + } + + + // Constructors + + /// + /// Initializes a new instance of this type. + /// + /// property value. + /// Initial property value. + /// property value. + public SingleSourceMapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) + { + ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); + ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); + + IsReadOnly = isReadOnly; + Descriptor = descriptor; + + this.map = map; + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index c6054aaaaf..7043284d80 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -196,16 +196,7 @@ public static Tuple Combine(this Tuple left, Tuple right) /// public static Tuple GetSegment(this Tuple tuple, in Segment segment) { - var length = segment.Length; - var map = new int[length]; - var fieldTypes = new Type[length]; - for (var index = 0; index < map.Length; index++) { - var sourceIndex = segment.Offset + index; - map[index] = sourceIndex; - fieldTypes[index] = tuple.Descriptor[sourceIndex]; - } - var descriptor = TupleDescriptor.Create(fieldTypes); - var transform = new MapTransform(false, descriptor, map); + var transform = new SegmentTransform(false, tuple.Descriptor, segment); return transform.Apply(TupleTransformType.TransformedTuple, tuple); } From 4eb72a02ab7c1621a3d5ed5758b76b69e090b766 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 27 Dec 2021 18:02:42 -0800 Subject: [PATCH 19/64] Do not create map for the SegmentTransform --- .../Transform/Internals/MapTransformTuple1.cs | 12 +-- .../Internals/SegmentTransformTuple.cs | 76 +++++++++++++++++++ .../Tuples/Transform/SegmentTransform.cs | 48 ++++++------ 3 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index d39653c9f8..2951bbd261 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -30,13 +30,13 @@ internal sealed class MapTransformTuple1 : TransformedTuple public override TupleFieldState GetFieldState(int fieldIndex) { - var index = GetMappedFieldIndex(fieldIndex); + var index = GetSourceFieldIndex(fieldIndex); return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - var index = GetMappedFieldIndex(fieldIndex); + var index = GetSourceFieldIndex(fieldIndex); if (index == MapTransform.NoMapping) { return; } @@ -46,7 +46,7 @@ protected internal override void SetFieldState(int fieldIndex, TupleFieldState f /// public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - int index = GetMappedFieldIndex(fieldIndex); + int index = GetSourceFieldIndex(fieldIndex); return index == MapTransform.NoMapping ? DefaultResult.GetValue(fieldIndex, out fieldState) : source.GetValue(index, out fieldState); @@ -58,12 +58,12 @@ public override void SetValue(int fieldIndex, object fieldValue) if (TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - source.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); + source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); } #endregion - private int GetMappedFieldIndex(int fieldIndex) + private int GetSourceFieldIndex(int fieldIndex) { var mappedIndex = TupleTransform.Map[fieldIndex]; return mappedIndex < 0 ? MapTransform.NoMapping : mappedIndex; @@ -74,7 +74,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, if (isWriting && TupleTransform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } - var index = GetMappedFieldIndex(fieldIndex); + var index = GetSourceFieldIndex(fieldIndex); return index == MapTransform.NoMapping ? default : source.GetMappedContainer(index, isWriting); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs new file mode 100644 index 0000000000..977562695e --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs @@ -0,0 +1,76 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using Xtensive.Core; + +namespace Xtensive.Tuples.Transform.Internals +{ + /// + /// A result tuple mapping 1 source tuple to a single one (this). + /// + [Serializable] + internal sealed class SegmentTransformTuple : TransformedTuple + { + private readonly Tuple source; + private Tuple defaultResult; + + /// + /// Gets the default result tuple. + /// Can be used to get default values for the result tuple fields. + /// + private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); + + #region GetFieldState, GetValue, SetValue methods + + /// + public override TupleFieldState GetFieldState(int fieldIndex) + { + var index = GetSourceFieldIndex(fieldIndex); + return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); + } + + protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) + { + var index = GetSourceFieldIndex(fieldIndex); + if (index == MapTransform.NoMapping) { + return; + } + source.SetFieldState(index, fieldState); + } + + /// + public override object GetValue(int fieldIndex, out TupleFieldState fieldState) + { + int index = GetSourceFieldIndex(fieldIndex); + return index == MapTransform.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : source.GetValue(index, out fieldState); + } + + /// + public override void SetValue(int fieldIndex, object fieldValue) + { + if (TupleTransform.IsReadOnly) { + throw Exceptions.ObjectIsReadOnly(null); + } + source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); + } + + #endregion + + private int GetSourceFieldIndex(int fieldIndex) + { + var sourceIndex = TupleTransform.Segment.Offset + fieldIndex; + return sourceIndex < 0 || sourceIndex >= source.Count ? MapTransform.NoMapping : sourceIndex; + } + + // Constructors + public SegmentTransformTuple(SegmentTransform transform, Tuple source) + : base(transform) + { + this.source = source; + } + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 4970854aae..18716077f0 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -5,11 +5,9 @@ // Created: 2008.05.20 using System; -using System.Diagnostics; using Xtensive.Core; using Xtensive.Reflection; - - +using Xtensive.Tuples.Transform.Internals; namespace Xtensive.Tuples.Transform { @@ -18,22 +16,20 @@ namespace Xtensive.Tuples.Transform /// public sealed class SegmentTransform : ITupleTransform { - private readonly SingleSourceMapTransform mapTransform; private readonly Segment segment; /// - public TupleDescriptor Descriptor => mapTransform.Descriptor; + public TupleDescriptor Descriptor { get; } /// - public bool IsReadOnly => mapTransform.IsReadOnly; + public bool IsReadOnly { get; } /// /// Gets the segment this transform extracts. /// - public Segment Segment + public ref readonly Segment Segment { - [DebuggerStepThrough] - get { return segment; } + get { return ref segment; } } /// @@ -46,7 +42,20 @@ public Segment Segment /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { - return mapTransform.Apply(transformType, source); + switch (transformType) { + case TupleTransformType.Auto: + if (source is ITransformedTuple) + goto case TupleTransformType.Tuple; + goto case TupleTransformType.TransformedTuple; + case TupleTransformType.TransformedTuple: + return new SegmentTransformTuple(this, source); + case TupleTransformType.Tuple: + Tuple result = Tuple.Create(Descriptor); + source.CopyTo(result, segment.Offset, segment.Length); + return result; + default: + throw new ArgumentOutOfRangeException(nameof(transformType)); + } } /// @@ -61,17 +70,6 @@ public override string ToString() // Constructors - private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDescriptor, in Segment segment, out int[] map) - { - var fields = new Type[segment.Length]; - map = new int[segment.Length]; - for (int i = 0, j = segment.Offset; i < segment.Length; i++, j++) { - fields[i] = sourceDescriptor[j]; - map[i] = j; - } - return TupleDescriptor.Create(fields); - } - /// /// Initializes a new instance of this type. /// @@ -80,7 +78,13 @@ private static TupleDescriptor CreateDescriptorAndMap(TupleDescriptor sourceDesc /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) { - mapTransform = new SingleSourceMapTransform(isReadOnly, CreateDescriptorAndMap(sourceDescriptor, segment, out var map), map); + IsReadOnly = isReadOnly; + + var fields = new Type[segment.Length]; + for (int i = 0, j = segment.Offset; i < segment.Length; i++, j++) { + fields[i] = sourceDescriptor[j]; + } + Descriptor = TupleDescriptor.Create(fields); this.segment = segment; } } From bd7fc842acf017579be45154ca67d3a87cfd9126 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 27 Dec 2021 20:33:23 -0800 Subject: [PATCH 20/64] Improve SegmentTransform.Apply method implementation --- .../Tuples/Transform/SegmentTransform.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 18716077f0..2f57a258b4 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -42,19 +42,18 @@ public ref readonly Segment Segment /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { - switch (transformType) { - case TupleTransformType.Auto: - if (source is ITransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new SegmentTransformTuple(this, source); - case TupleTransformType.Tuple: - Tuple result = Tuple.Create(Descriptor); - source.CopyTo(result, segment.Offset, segment.Length); - return result; - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); + return transformType switch { + TupleTransformType.Auto when source is ITransformedTuple => CopySourceSegment(source), + TupleTransformType.Auto => new SegmentTransformTuple(this, source), + TupleTransformType.Tuple => CopySourceSegment(source), + TupleTransformType.TransformedTuple => new SegmentTransformTuple(this, source), + _ => throw new ArgumentOutOfRangeException(nameof(transformType)) + }; + + Tuple CopySourceSegment(Tuple source) { + var result = Tuple.Create(Descriptor); + source.CopyTo(result, segment.Offset, segment.Length); + return result; } } From 3280bbb5ae6328510e461c1dd1f3d091c2d1cb4b Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 27 Dec 2021 20:42:31 -0800 Subject: [PATCH 21/64] Improve SegmentTransform constructor implementation --- Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs | 7 ++----- Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 2f57a258b4..466ae95198 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -79,11 +79,8 @@ public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Se { IsReadOnly = isReadOnly; - var fields = new Type[segment.Length]; - for (int i = 0, j = segment.Offset; i < segment.Length; i++, j++) { - fields[i] = sourceDescriptor[j]; - } - Descriptor = TupleDescriptor.Create(fields); + var fields = new ArraySegment(sourceDescriptor.FieldTypes, segment.Offset, segment.Length); + Descriptor = TupleDescriptor.Create(fields.ToArray()); this.segment = segment; } } diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 219cc68099..8026119d59 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs @@ -35,7 +35,7 @@ public sealed class TupleDescriptor : IEquatable, IReadOnlyList internal readonly PackedFieldDescriptor[] FieldDescriptors; [field: NonSerialized] - private Type[] FieldTypes { get; } + internal Type[] FieldTypes { get; } /// /// Gets the empty tuple descriptor. From f48d39fa103c8c7b1bd0d858e0fadf84000b1240 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 27 Dec 2021 20:53:29 -0800 Subject: [PATCH 22/64] Improve MapTransform constructor implementation --- Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 71ceba9096..c13c21121e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Xtensive.Collections; using Xtensive.Core; using Xtensive.Reflection; @@ -104,19 +105,9 @@ public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[ IsReadOnly = isReadOnly; Descriptor = descriptor; - var newFirstSourceMap = new int[map.Length]; - var index = 0; - var newSourceCount = -1; - foreach (var mappedTo in map) { - if (mappedTo.First > newSourceCount) { - newSourceCount = mappedTo.First; - } - newFirstSourceMap[index++] = mappedTo.First == 0 ? mappedTo.Second : -1; - } - newSourceCount++; this.map = map; - sourceCount = newSourceCount; + sourceCount = map.Max(item => item.First) + 1; } } } \ No newline at end of file From 14690cb3d229fc4c147d0874505801f28d9fc625 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 15:18:28 -0800 Subject: [PATCH 23/64] Rename the CombineTransform class to the ConcatTransform --- .../Tuples/Transform/MergeTransformTest.cs | 6 +++--- Orm/Xtensive.Orm/Orm/Entity.cs | 2 +- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs | 2 +- .../Orm/Rse/Providers/Compilable/IncludeProvider.cs | 4 ++-- Orm/Xtensive.Orm/Orm/VersionInfo.cs | 6 +++--- .../Transform/{CombineTransform.cs => ConcatTransform.cs} | 6 +++--- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) rename Orm/Xtensive.Orm/Tuples/Transform/{CombineTransform.cs => ConcatTransform.cs} (92%) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs index 3b036ac85a..02ddc7aa71 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs @@ -23,8 +23,8 @@ public void BaseTest() Xtensive.Tuples.Tuple t2 = Xtensive.Tuples.Tuple.Create(3, 4.0, "5"); TestLog.Info("Originals: {0}, {1}", t1, t2); - CombineTransform mt = new CombineTransform(false, t1.Descriptor, t2.Descriptor); - CombineTransform mtro = new CombineTransform(true, t1.Descriptor, t2.Descriptor); + ConcatTransform mt = new ConcatTransform(false, t1.Descriptor, t2.Descriptor); + ConcatTransform mtro = new ConcatTransform(true, t1.Descriptor, t2.Descriptor); Xtensive.Tuples.Tuple wt1 = mt.Apply(TupleTransformType.TransformedTuple, t1, t2); TestLog.Info("Wrapper: {0}", wt1); @@ -62,7 +62,7 @@ public void PerformanceTest1() { AdvancedComparerStruct comparer = AdvancedComparerStruct.Default; Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1); - CombineTransform mt = new CombineTransform(false, t.Descriptor, t.Descriptor); + ConcatTransform mt = new ConcatTransform(false, t.Descriptor, t.Descriptor); Xtensive.Tuples.Tuple wt1 = mt.Apply(TupleTransformType.TransformedTuple, t, t); Xtensive.Tuples.Tuple wt2 = mt.Apply(TupleTransformType.TransformedTuple, t, t); Xtensive.Tuples.Tuple ct1 = mt.Apply(TupleTransformType.Tuple, t, t); diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index 0c65643a1b..e5aa63f0d5 100644 --- a/Orm/Xtensive.Orm/Orm/Entity.cs +++ b/Orm/Xtensive.Orm/Orm/Entity.cs @@ -127,7 +127,7 @@ public VersionInfo VersionInfo { foreach (var root in ((IHasVersionRoots) this).GetVersionRoots()) { if (root is IHasVersionRoots) throw new InvalidOperationException(Strings.ExVersionRootObjectCantImplementIHasVersionRoots); - version = version.Combine(root.Key, root.VersionInfo); + version = version.Concat(root.Key, root.VersionInfo); } return version; } diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index e8c2795ffa..33c6e82e3f 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -45,7 +45,7 @@ public abstract class EntitySetBase : SessionBound, private static readonly Func EntitySetTypeStateFactory = BuildEntitySetTypeState; private readonly Entity owner; - private readonly CombineTransform auxilaryTypeKeyTransform; + private readonly ConcatTransform auxilaryTypeKeyTransform; private readonly bool skipOwnerVersionChange; private bool isInitialized; @@ -1033,7 +1033,7 @@ protected EntitySetBase(Entity owner, FieldInfo field) if (association.AuxiliaryType != null && association.IsMaster) { var domain = Session.Domain; var itemType = domain.Model.Types[Field.ItemType]; - auxilaryTypeKeyTransform = new CombineTransform( + auxilaryTypeKeyTransform = new ConcatTransform( false, owner.TypeInfo.Key.TupleDescriptor, itemType.Key.TupleDescriptor); diff --git a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs index 615f42d08a..5499d5184d 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs @@ -54,7 +54,7 @@ private void RemapEntitySetReference(RemapContext context, ReferenceFieldChangeI var fieldOwnerKey = context.TryRemapKey(info.FieldOwner); var fieldValueKey = context.TryRemapKey(info.FieldValue); - var transformer = new CombineTransform(false, fieldOwnerKey.Value.Descriptor, fieldValueKey.Value.Descriptor); + var transformer = new ConcatTransform(false, fieldOwnerKey.Value.Descriptor, fieldValueKey.Value.Descriptor); var combinedTuple = transformer.Apply(TupleTransformType.Tuple, fieldOwnerKey.Value, fieldValueKey.Value); var newCombinedKey = Key.Create(Session.Domain, Session.StorageNodeId, fieldAssociation.AuxiliaryType, TypeReferenceAccuracy.ExactType, combinedTuple); diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 19155f1e36..839329d110 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -55,7 +55,7 @@ public sealed class IncludeProvider: UnaryProvider, public SingleSourceMapTransform FilteredColumnsExtractionTransform { get; private set; } - public CombineTransform ResultTransform { get; private set; } + public ConcatTransform ResultTransform { get; private set; } private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); @@ -69,7 +69,7 @@ protected override RecordSetHeader BuildHeader() } var tupleDescriptor = TupleDescriptor.Create(fieldTypes); FilteredColumnsExtractionTransform = new SingleSourceMapTransform(true, tupleDescriptor, FilteredColumns); - ResultTransform = new CombineTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); + ResultTransform = new ConcatTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); return newHeader; } diff --git a/Orm/Xtensive.Orm/Orm/VersionInfo.cs b/Orm/Xtensive.Orm/Orm/VersionInfo.cs index ff38fcc6c9..3d3c950bf0 100644 --- a/Orm/Xtensive.Orm/Orm/VersionInfo.cs +++ b/Orm/Xtensive.Orm/Orm/VersionInfo.cs @@ -56,7 +56,7 @@ internal Tuple Value { /// The key to combine. /// The version info to combine. /// Combined version info. - internal VersionInfo Combine(Key key, VersionInfo versionInfo) + internal VersionInfo Concat(Key key, VersionInfo versionInfo) { ArgumentValidator.EnsureArgumentNotNull(key, "key"); @@ -64,9 +64,9 @@ internal VersionInfo Combine(Key key, VersionInfo versionInfo) if (resultVersion==null) resultVersion = key.Value; else - resultVersion = resultVersion.Combine(key.Value); + resultVersion = resultVersion.Concat(key.Value); if (!versionInfo.IsVoid) - resultVersion = resultVersion.Combine(versionInfo.Value); + resultVersion = resultVersion.Concat(versionInfo.Value); return new VersionInfo(resultVersion.ToRegular()); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs similarity index 92% rename from Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs rename to Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index 5552cf838e..9355ef1963 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -11,10 +11,10 @@ namespace Xtensive.Tuples.Transform { /// - /// This class is used for source s combining. + /// This class is used for concatenation of two s. /// [Serializable] - public sealed class CombineTransform : ITupleTransform + public sealed class ConcatTransform : ITupleTransform { private MapTransform mapTransform; private readonly (TupleDescriptor first, TupleDescriptor second) sources; @@ -71,7 +71,7 @@ private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, /// property value. /// First tuple descriptor to combine. /// Second tuple descriptor to combine. - public CombineTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) + public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) { mapTransform = new MapTransform(isReadOnly, CreateDescriptorAndMap((first, second), out var map), map); this.sources = (first, second); diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index 7043284d80..088e4baed1 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -182,9 +182,9 @@ internal static void CopyTo(in this FixedReadOnlyList3 sources, Tuple tar /// The first to combine. /// The second to combine. /// - public static Tuple Combine(this Tuple left, Tuple right) + public static Tuple Concat(this Tuple left, Tuple right) { - var transform = new CombineTransform(false, left.Descriptor, right.Descriptor); + var transform = new ConcatTransform(false, left.Descriptor, right.Descriptor); return transform.Apply(TupleTransformType.TransformedTuple, left, right); } From 10f9388b589df93e6d59d9cac9f23bdd09d89b08 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 15:57:25 -0800 Subject: [PATCH 24/64] Do not create map for the ConcatTransform --- .../Tuples/Transform/ConcatTransform.cs | 51 +++++------ .../Internals/ConcatTransformTuple.cs | 84 +++++++++++++++++++ 2 files changed, 111 insertions(+), 24 deletions(-) create mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index 9355ef1963..be33e8777e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -5,8 +5,8 @@ // Created: 2008.04.30 using System; -using Xtensive.Core; using Xtensive.Reflection; +using Xtensive.Tuples.Transform.Internals; namespace Xtensive.Tuples.Transform { @@ -16,14 +16,13 @@ namespace Xtensive.Tuples.Transform [Serializable] public sealed class ConcatTransform : ITupleTransform { - private MapTransform mapTransform; private readonly (TupleDescriptor first, TupleDescriptor second) sources; /// - public TupleDescriptor Descriptor => mapTransform.Descriptor; + public TupleDescriptor Descriptor { get; } /// - public bool IsReadOnly => mapTransform.IsReadOnly; + public bool IsReadOnly { get; } /// /// Applies the transformation. @@ -34,8 +33,22 @@ public sealed class ConcatTransform : ITupleTransform /// Transformation result - /// either or descendant, /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) - => mapTransform.Apply(transformType, source1, source2); + public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) { + return transformType switch { + TupleTransformType.Auto when source1 is ITransformedTuple || source2 is ITransformedTuple => CopySourceTuples(source1, source2), + TupleTransformType.Auto => new ConcatTransformTuple(this, source1, source2), + TupleTransformType.Tuple => CopySourceTuples(source1, source2), + TupleTransformType.TransformedTuple => new ConcatTransformTuple(this, source1, source2), + _ => throw new ArgumentOutOfRangeException(nameof(transformType)) + }; + + Tuple CopySourceTuples(Tuple source1, Tuple source2) { + var result = Tuple.Create(Descriptor); + source1.CopyTo(result); + source2.CopyTo(result, 0, source1.Count, source2.Count); + return result; + } + } /// public override string ToString() @@ -46,24 +59,8 @@ public override string ToString() description); } - // Constructors - private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, TupleDescriptor second) sources, out Pair[] map) - { - int totalLength = sources.first.Count + sources.second.Count; - var types = new Type[totalLength]; - map = new Pair[totalLength]; - int index = 0; - for (int i = 0; i < 2; i++) { - var currentDescriptor = i == 0 ? sources.first : sources.second; - int currentCount = currentDescriptor.Count; - for (int j = 0; j < currentCount; j++) { - types[index] = currentDescriptor[j]; - map[index++] = new Pair(i, j); - } - } - return TupleDescriptor.Create(types); - } + // Constructors /// /// Initializes a new instance of this type. @@ -73,7 +70,13 @@ private static TupleDescriptor CreateDescriptorAndMap(in (TupleDescriptor first, /// Second tuple descriptor to combine. public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) { - mapTransform = new MapTransform(isReadOnly, CreateDescriptorAndMap((first, second), out var map), map); + var (firstCount, secondCount) = (first.Count, second.Count); + var types = new Type[firstCount + secondCount]; + Array.Copy(first.FieldTypes, types, firstCount); + Array.Copy(second.FieldTypes, 0, types, firstCount, secondCount); + + IsReadOnly = isReadOnly; + Descriptor = TupleDescriptor.Create(types); this.sources = (first, second); } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs new file mode 100644 index 0000000000..5c881ede55 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs @@ -0,0 +1,84 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +using System; +using Xtensive.Core; + +namespace Xtensive.Tuples.Transform.Internals +{ + /// + /// A result tuple mapping 1 source tuple to a single one (this). + /// + [Serializable] + internal sealed class ConcatTransformTuple : TransformedTuple + { + private readonly Tuple source1; + private readonly Tuple source2; + private readonly int totalCount; + private Tuple defaultResult; + + /// + /// Gets the default result tuple. + /// Can be used to get default values for the result tuple fields. + /// + private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); + + #region GetFieldState, GetValue, SetValue methods + + /// + public override TupleFieldState GetFieldState(int fieldIndex) + { + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); + } + + protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) + { + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + if (index == MapTransform.NoMapping) { + return; + } + source.SetFieldState(index, fieldState); + } + + /// + public override object GetValue(int fieldIndex, out TupleFieldState fieldState) + { + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + return index == MapTransform.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : source.GetValue(index, out fieldState); + } + + /// + public override void SetValue(int fieldIndex, object fieldValue) + { + if (TupleTransform.IsReadOnly) { + throw Exceptions.ObjectIsReadOnly(null); + } + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + source.SetValue(index, fieldValue); + } + + #endregion + + private (Tuple source, int index) GetSourceAndFieldIndex(int fieldIndex) + { + if (fieldIndex < 0 || fieldIndex > totalCount) { + return (null, MapTransform.NoMapping); + } + var source2Index = fieldIndex - source1.Count; + return source2Index < 0 ? (source1, fieldIndex) : (source2, source2Index); + } + + // Constructors + public ConcatTransformTuple(ConcatTransform transform, Tuple source1, Tuple source2) + : base(transform) + { + this.source1 = source1; + this.source2 = source2; + totalCount = source1.Count + source2.Count; + } + } +} \ No newline at end of file From 3fe1b5c8ee63dd1988175aeeb338759fc8e76735 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 16:28:24 -0800 Subject: [PATCH 25/64] Do not use MapTransform to inject primary keys to the entity tuples --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index f02b100f16..5150f1c715 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -56,7 +56,6 @@ public sealed class TypeInfo : SchemaMappedNode private HierarchyInfo hierarchy; private int typeId = NoTypeId; private object typeDiscriminatorValue; - private MapTransform primaryKeyInjector; private bool isLeaf; private bool isOutboundOnly; private bool isInboundOnly; @@ -409,9 +408,10 @@ internal set /// public Tuple CreateEntityTuple(Tuple primaryKey, int typeIdValue) { - var result = primaryKeyInjector.Apply(TupleTransformType.Tuple, primaryKey, TuplePrototype); - if (typeIdField!=null) + var result = InjectPrimaryKey(TuplePrototype, primaryKey); + if (typeIdField!=null) { result.SetValue(typeIdField.MappingInfo.Offset, typeIdValue); + } return result; } @@ -426,7 +426,11 @@ public Tuple CreateEntityTuple(Tuple primaryKey, int typeIdValue) /// public Tuple InjectPrimaryKey(Tuple entityTuple, Tuple primaryKey) { - return primaryKeyInjector.Apply(TupleTransformType.Tuple, primaryKey, entityTuple); + var result = Tuple.Create(TupleDescriptor); + var primaryKeyCount = primaryKey.Count; + primaryKey.CopyTo(result, 0, primaryKeyCount); + entityTuple.CopyTo(result, primaryKeyCount, primaryKeyCount, entityTuple.Count - primaryKeyCount); + return result; } /// @@ -795,13 +799,6 @@ private void BuildTuplePrototype() if (Hierarchy.TypeDiscriminatorMap!=null) tuple.SetValue(Hierarchy.TypeDiscriminatorMap.Field.MappingInfo.Offset, typeDiscriminatorValue); - // Building primary key injector - var fieldCount = TupleDescriptor.Count; - var keyFieldCount = Key.TupleDescriptor.Count; - var keyFieldMap = new Pair[fieldCount]; - for (i = 0; i < fieldCount; i++) - keyFieldMap[i] = new Pair((i < keyFieldCount) ? 0 : 1, i); - primaryKeyInjector = new MapTransform(false, TupleDescriptor, keyFieldMap); } TuplePrototype = IsEntity ? tuple.ToFastReadOnly() : tuple; } From f2a176e163ce1e986b19bcf92bd2c9ba98597756 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 18:00:16 -0800 Subject: [PATCH 26/64] Do not use MapTransform to create seek keys in the EntitySetBase --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 68 +++++++++++++------ .../Orm/Internals/EntitySetTypeState.cs | 6 +- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 33c6e82e3f..a12755895c 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -903,8 +903,7 @@ private bool Contains(Key key, Entity item) var entitySetTypeState = GetEntitySetTypeState(); var parameterContext = new ParameterContext(); - parameterContext.SetValue(keyParameter, entitySetTypeState.SeekTransform - .Apply(TupleTransformType.TransformedTuple, Owner.Key.Value, key.Value)); + parameterContext.SetValue(keyParameter, entitySetTypeState.SeekKeyBuilder.Invoke(Owner.Key.Value, key.Value)); using (var recordSetReader = entitySetTypeState.SeekProvider.GetRecordSetReader(Session, parameterContext)) { foundInDatabase = recordSetReader.MoveNext(); } @@ -934,25 +933,35 @@ private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, Entit var seek = entitySet.Session.Compile(query); var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; var targetDescriptor = association.TargetType.Key.TupleDescriptor; + var ownerFieldCount = ownerDescriptor.Count; - var itemColumnOffsets = association.AuxiliaryType == null - ? association.UnderlyingIndex.ValueColumns + Type[] keyFieldTypes; + IReadOnlyList itemColumnOffsets; + if (association.AuxiliaryType == null) { + itemColumnOffsets = Enumerable.Repeat(-1, ownerFieldCount).Concat( + association.UnderlyingIndex.ValueColumns .Where(ci => ci.IsPrimaryKey) - .Select(ci => ci.Field.MappingInfo.Offset) - .ToList() - : Enumerable.Range(0, targetDescriptor.Count).ToList(); - - var keyFieldCount = ownerDescriptor.Count + itemColumnOffsets.Count; - var keyFieldTypes = ownerDescriptor - .Concat(itemColumnOffsets.Select(i => targetDescriptor[i])) - .ToArray(keyFieldCount); - var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); + .Select(ci => ci.Field.MappingInfo.Offset)).ToList(); + var keyFieldCount = itemColumnOffsets.Count; + keyFieldTypes = new Type[keyFieldCount]; + Array.Copy(ownerDescriptor.FieldTypes, keyFieldTypes, ownerFieldCount); + for (var index=ownerFieldCount; index new Pair(0, i)) - .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) - .ToArray(keyFieldCount); - var seekTransform = new MapTransform(true, keyDescriptor, map); + var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); Func itemCtor = null; if (association.AuxiliaryType != null) { @@ -961,7 +970,28 @@ private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, Entit Array.Empty()); } - return new EntitySetTypeState(seek, seekTransform, itemCtor, entitySet.GetItemCountQueryDelegate(field)); + var seekKeyTupleBuilder = new SeekKeyTupleBuilder(keyDescriptor, itemColumnOffsets); + return new EntitySetTypeState(seek, seekKeyTupleBuilder.Build, itemCtor, entitySet.GetItemCountQueryDelegate(field)); + } + + private class SeekKeyTupleBuilder + { + private readonly TupleDescriptor keyDescriptor; + private readonly IReadOnlyList itemColumnOffsets; + + public Tuple Build(Tuple ownerKeyTuple, Tuple itemTuple) + { + var result = Tuple.Create(keyDescriptor); + ownerKeyTuple.CopyTo(result); + itemTuple.CopyTo(result, itemColumnOffsets); + return result; + } + + public SeekKeyTupleBuilder(TupleDescriptor keyDescriptor, IReadOnlyList itemColumnOffsets) + { + this.keyDescriptor = keyDescriptor; + this.itemColumnOffsets = itemColumnOffsets; + } } private int? GetItemIndex(EntitySetState state, Key key) diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs index 29c75b99f9..0449ab413e 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs @@ -19,17 +19,17 @@ internal sealed class EntitySetTypeState { public readonly ExecutableProvider SeekProvider; - public readonly MapTransform SeekTransform; + public readonly Func SeekKeyBuilder; public readonly Func ItemCtor; public readonly Func ItemCountQuery; - public EntitySetTypeState(ExecutableProvider seekProvider, MapTransform seekTransform, + public EntitySetTypeState(ExecutableProvider seekProvider, Func seekKeyBuilder, Func itemCtor, Func itemCountQuery) { SeekProvider = seekProvider; - SeekTransform = seekTransform; + SeekKeyBuilder = seekKeyBuilder; ItemCtor = itemCtor; ItemCountQuery = itemCountQuery; } From 283d37eae7926ffa30730e03e1197d2153eb78b3 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 19:11:19 -0800 Subject: [PATCH 27/64] Delete the MapTransform class and its dependencies --- .../Internals/FixedReadOnlyList3.cs | 84 ------------- .../Materialization/MaterializationHelper.cs | 3 +- .../Providers/Compilable/CalculateProvider.cs | 2 +- .../Providers/Compilable/RowNumberProvider.cs | 2 +- .../Internals/ConcatTransformTuple.cs | 10 +- .../Transform/Internals/MapTransformTuple1.cs | 12 +- .../Transform/Internals/MapTransformTuple3.cs | 93 -------------- .../Internals/SegmentTransformTuple.cs | 10 +- .../Tuples/Transform/MapTransform.cs | 113 ----------------- .../Tuples/Transform/TransformUtil.cs | 18 +++ Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 115 ------------------ 11 files changed, 37 insertions(+), 425 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs create mode 100644 Orm/Xtensive.Orm/Tuples/Transform/TransformUtil.cs diff --git a/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs b/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs deleted file mode 100644 index 8426b58454..0000000000 --- a/Orm/Xtensive.Orm/Collections/Internals/FixedReadOnlyList3.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2007.10.20 - -using System; -using System.Diagnostics; -using Xtensive.Core; - - -namespace Xtensive.Collections -{ - /// - /// Defines a fixed stack-like list with three items. - /// - /// Type of items. - [Serializable] - [DebuggerDisplay("Count = {Count}")] - internal readonly struct FixedReadOnlyList3 - { - private readonly (T n1, T n2, T n3) slots; - private readonly int count; - - /// - /// Gets count of items. - /// - public int Count => count; - - /// - /// Gets or sets the element at the specified index. - /// - /// Index of the item - /// The index is greater or equal count of items. - public T this[int index] - { - get { - if (index < 0 || index >= count) { - ArgumentValidator.EnsureArgumentIsInRange(index, 0, count - 1, nameof(index)); - } - return index switch { - 0 => slots.n1, - 1 => slots.n2, - _ => slots.n3 - }; - } - } - - - /// - /// Initializes a new instance of this type. - /// - /// Item to add. - public FixedReadOnlyList3(T item) - { - slots = (item, default, default); - count = 1; - } - - - /// - /// Initializes a new instance of this type. - /// - /// First item to add. - /// Second item ot add. - public FixedReadOnlyList3(T first, T second) - { - slots = (first, second, default); - count = 2; - } - - /// - /// Initializes a new instance of this type. - /// - /// First item to add. - /// Second item ot add. - /// Third item to add. - public FixedReadOnlyList3(T first, T second, T third) - { - slots = (first, second, third); - count = 3; - } - } -} diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationHelper.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationHelper.cs index 025523cf85..b39428fcaf 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationHelper.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationHelper.cs @@ -12,7 +12,6 @@ using Xtensive.Core; using Xtensive.Orm.Internals.Prefetch; using Xtensive.Orm.Rse; -using Xtensive.Reflection; using Xtensive.Tuples; using Xtensive.Tuples.Transform; using EnumerationContext = Xtensive.Orm.Providers.EnumerationContext; @@ -46,7 +45,7 @@ internal static class MaterializationHelper public static int[] CreateSingleSourceMap(int targetLength, IReadOnlyList> remappedColumns) { var map = new int[targetLength]; - Array.Fill(map, MapTransform.NoMapping); + Array.Fill(map, TransformUtil.NoMapping); for (var i = 0; i < remappedColumns.Count; i++) { var remappedColumn = remappedColumns[i]; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index d55c29db8f..f88c21705e 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -53,7 +53,7 @@ protected override void Initialize() base.Initialize(); var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) - columnIndexes[i] = (i < Source.Header.Length) ? i : MapTransform.NoMapping; + columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 39229ec221..c7ac80acbd 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -33,7 +33,7 @@ protected override void Initialize() base.Initialize(); var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) - columnIndexes[i] = (i < Source.Header.Length) ? i : MapTransform.NoMapping; + columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs index 5c881ede55..d8e2dae7ad 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs @@ -8,7 +8,7 @@ namespace Xtensive.Tuples.Transform.Internals { /// - /// A result tuple mapping 1 source tuple to a single one (this). + /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] internal sealed class ConcatTransformTuple : TransformedTuple @@ -30,13 +30,13 @@ internal sealed class ConcatTransformTuple : TransformedTuple public override TupleFieldState GetFieldState(int fieldIndex) { var (source, index) = GetSourceAndFieldIndex(fieldIndex); - return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); + return index == TransformUtil.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { var (source, index) = GetSourceAndFieldIndex(fieldIndex); - if (index == MapTransform.NoMapping) { + if (index == TransformUtil.NoMapping) { return; } source.SetFieldState(index, fieldState); @@ -46,7 +46,7 @@ protected internal override void SetFieldState(int fieldIndex, TupleFieldState f public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { var (source, index) = GetSourceAndFieldIndex(fieldIndex); - return index == MapTransform.NoMapping + return index == TransformUtil.NoMapping ? DefaultResult.GetValue(fieldIndex, out fieldState) : source.GetValue(index, out fieldState); } @@ -66,7 +66,7 @@ public override void SetValue(int fieldIndex, object fieldValue) private (Tuple source, int index) GetSourceAndFieldIndex(int fieldIndex) { if (fieldIndex < 0 || fieldIndex > totalCount) { - return (null, MapTransform.NoMapping); + return (null, TransformUtil.NoMapping); } var source2Index = fieldIndex - source1.Count; return source2Index < 0 ? (source1, fieldIndex) : (source2, source2Index); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs index 2951bbd261..6ef4bb24d8 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs @@ -11,7 +11,7 @@ namespace Xtensive.Tuples.Transform.Internals { /// - /// A result tuple mapping 1 source tuple to a single one (this). + /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] internal sealed class MapTransformTuple1 : TransformedTuple @@ -31,13 +31,13 @@ internal sealed class MapTransformTuple1 : TransformedTuple GetMappedContainer(int fieldIndex, bool isWriting) @@ -75,7 +75,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, throw Exceptions.ObjectIsReadOnly(null); } var index = GetSourceFieldIndex(fieldIndex); - return index == MapTransform.NoMapping ? default : source.GetMappedContainer(index, isWriting); + return index == TransformUtil.NoMapping ? default : source.GetMappedContainer(index, isWriting); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs deleted file mode 100644 index 82a69ee555..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alex Yakunin -// Created: 2008.06.04 - -using System; -using Xtensive.Collections; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform.Internals -{ - /// - /// A result tuple mapping up to 3 source tuples to a single one (this). - /// - [Serializable] - internal sealed class MapTransformTuple3 : TransformedTuple - { - private readonly FixedReadOnlyList3 tuples; - - #region GetFieldState, GetValue, SetValue methods - - /// - public override TupleFieldState GetFieldState(int fieldIndex) - { - var indexes = TupleTransform.map[fieldIndex]; - return tuples[indexes.First].GetFieldState(indexes.Second); - } - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - var indexes = TupleTransform.map[fieldIndex]; - tuples[indexes.First].SetFieldState(indexes.Second, fieldState); - } - - /// - public override object GetValue(int fieldIndex, out TupleFieldState fieldState) - { - var indexes = TupleTransform.map[fieldIndex]; - return tuples[indexes.First].GetValue(indexes.Second, out fieldState); - } - - /// - public override void SetValue(int fieldIndex, object fieldValue) - { - if (TupleTransform.IsReadOnly) { - throw Exceptions.ObjectIsReadOnly(null); - } - var indexes = TupleTransform.map[fieldIndex]; - tuples[indexes.First].SetValue(indexes.Second, fieldValue); - } - - #endregion - - protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) - { - if (isWriting && TupleTransform.IsReadOnly) { - throw Exceptions.ObjectIsReadOnly(null); - } - var map = TupleTransform.map[fieldIndex]; - return tuples[map.First].GetMappedContainer(map.Second, isWriting); - } - - - // Constructors - - /// - /// Initializes new instance of this type. - /// - /// The transform. - /// First source tuple. - /// 2nd source tuple. - public MapTransformTuple3(MapTransform transform, Tuple source1, Tuple source2) - : base(transform) - { - tuples = new FixedReadOnlyList3(source1, source2); - } - - /// - /// Initializes new instance of this type. - /// - /// The transform. - /// First source tuple. - /// 2nd source tuple. - /// 3rd source tuple. - public MapTransformTuple3(MapTransform transform, Tuple source1, Tuple source2, Tuple source3) - : base(transform) - { - tuples = new FixedReadOnlyList3(source1, source2, source3); - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs index 977562695e..258e5b858d 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs @@ -8,7 +8,7 @@ namespace Xtensive.Tuples.Transform.Internals { /// - /// A result tuple mapping 1 source tuple to a single one (this). + /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] internal sealed class SegmentTransformTuple : TransformedTuple @@ -28,13 +28,13 @@ internal sealed class SegmentTransformTuple : TransformedTuple public override TupleFieldState GetFieldState(int fieldIndex) { var index = GetSourceFieldIndex(fieldIndex); - return index == MapTransform.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); + return index == TransformUtil.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { var index = GetSourceFieldIndex(fieldIndex); - if (index == MapTransform.NoMapping) { + if (index == TransformUtil.NoMapping) { return; } source.SetFieldState(index, fieldState); @@ -44,7 +44,7 @@ protected internal override void SetFieldState(int fieldIndex, TupleFieldState f public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { int index = GetSourceFieldIndex(fieldIndex); - return index == MapTransform.NoMapping + return index == TransformUtil.NoMapping ? DefaultResult.GetValue(fieldIndex, out fieldState) : source.GetValue(index, out fieldState); } @@ -63,7 +63,7 @@ public override void SetValue(int fieldIndex, object fieldValue) private int GetSourceFieldIndex(int fieldIndex) { var sourceIndex = TupleTransform.Segment.Offset + fieldIndex; - return sourceIndex < 0 || sourceIndex >= source.Count ? MapTransform.NoMapping : sourceIndex; + return sourceIndex < 0 || sourceIndex >= source.Count ? TransformUtil.NoMapping : sourceIndex; } // Constructors diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs deleted file mode 100644 index c13c21121e..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.05.07 - -using System; -using System.Collections.Generic; -using System.Linq; -using Xtensive.Collections; -using Xtensive.Core; -using Xtensive.Reflection; - - -using Xtensive.Tuples.Transform.Internals; - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for any tuple field mapping transform. - /// Maps fields of destination tuple to fields of a set of source tuples. - /// - [Serializable] - public class MapTransform : ITupleTransform - { - private int sourceCount; - internal Pair[] map; - - /// - /// Gets describing the tuples - /// this transform may produce. - /// - public TupleDescriptor Descriptor { get; } - - /// - /// Indicates whether transform always produces read-only tuples or not. - /// > - public bool IsReadOnly { get; } - - /// - /// Means that no mapping is available for the specified field index. - /// - public const int NoMapping = int.MinValue; - - /// - /// Gets or sets destination-to-source field map. - /// - public IReadOnlyList> Map => map; - - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// First transformation source. - /// Second transformation source. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) - { - if (sourceCount > 2) { - throw new InvalidOperationException(string.Format(Strings.ExTheNumberOfSourcesIsTooSmallExpected, sourceCount)); - } - switch (transformType) { - case TupleTransformType.Auto: - if (source1 is ITransformedTuple) - goto case TupleTransformType.Tuple; - if (source2 is ITransformedTuple) - goto case TupleTransformType.Tuple; - goto case TupleTransformType.TransformedTuple; - case TupleTransformType.TransformedTuple: - return new MapTransformTuple3(this, source1, source2); - case TupleTransformType.Tuple: - var sources = new FixedReadOnlyList3(source1, source2); - Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); - return result; - default: - throw new ArgumentOutOfRangeException(nameof(transformType)); - } - } - - /// - public override string ToString() - { - string description = $"{sourceCount}: {map.ToCommaDelimitedString()}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, - GetType().GetShortName(), - description); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Initial property value. - /// property value. - public MapTransform(bool isReadOnly, TupleDescriptor descriptor, Pair[] map) - { - ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); - ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); - - IsReadOnly = isReadOnly; - Descriptor = descriptor; - - this.map = map; - sourceCount = map.Max(item => item.First) + 1; - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformUtil.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformUtil.cs new file mode 100644 index 0000000000..f8f4eeb33f --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/TransformUtil.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2021 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. + +namespace Xtensive.Tuples.Transform +{ + /// + /// Utility class holdind helper constants and methods for the transformation. + /// + public static class TransformUtil + { + + /// + /// Means that no mapping is available for the specified field index. + /// + public const int NoMapping = int.MinValue; + } +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index 088e4baed1..175b75192e 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -7,7 +7,6 @@ using System; using System.Collections; using System.Collections.Generic; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Tuples.Packed; @@ -104,74 +103,6 @@ public static void CopyTo(this Tuple source, Tuple target, IReadOnlyList ma } } - /// - /// Copies a set of elements from s - /// to using - /// specified target-to-source field index . - /// - /// Source tuples to copy. - /// Tuple that receives the data. - /// Target-to-source field index map. - /// Negative value in this map means "skip this element". - public static void CopyTo(this Tuple[] sources, Tuple target, Pair[] map) - { - if (target is PackedTuple packedTarget) { - var haveSlowSource = false; - var packedSources = new PackedTuple[sources.Length]; - - for (int i = 0; i < sources.Length; i++) { - if (sources[i] is PackedTuple packedSource) { - packedSources[i] = packedSource; - } - else { - haveSlowSource = true; - break; - } - } - - if (!haveSlowSource) { - CopyTupleArrayWithMappingFast(packedSources, packedTarget, map); - return; - } - } - - CopyTupleArrayWithMappingSlow(sources, target, map); - } - - /// - /// Copies a set of elements from s - /// to using - /// specified target-to-source field index . - /// - /// Source tuples to copy. - /// Tuple that receives the data. - /// Target-to-source field index map. - /// Negative value in this map means "skip this element". - internal static void CopyTo(in this FixedReadOnlyList3 sources, Tuple target, Pair[] map) - { - if (target is PackedTuple packedTarget) { - var count = sources.Count; - if (count > 0 && sources[0] is PackedTuple packedFirst) { - if (count==1) { - Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst), packedTarget, map); - return; - } - else if (sources[1] is PackedTuple packedSecond) { - if (count==2) { - Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst, packedSecond), packedTarget, map); - return; - } - else if (sources[2] is PackedTuple packedThird) { - Copy3TuplesWithMappingFast(new FixedReadOnlyList3(packedFirst, packedSecond, packedThird), packedTarget, map); - return; - } - } - } - } - - Copy3TuplesWithMappingSlow(sources, target, map); - } - #endregion #region Transforms @@ -466,52 +397,6 @@ private static void CopyTupleWithMappingFast(PackedTuple source, PackedTuple tar } } - private static void CopyTupleArrayWithMappingSlow(Tuple[] sources, Tuple target, Pair[] map) - { - for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { - var sourceInfo = map[targetIndex]; - var sourceTupleIndex = sourceInfo.First; - var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) - CopyValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); - } - } - - private static void CopyTupleArrayWithMappingFast(PackedTuple[] sources, PackedTuple target, Pair[] map) - { - for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { - var sourceInfo = map[targetIndex]; - var sourceTupleIndex = sourceInfo.First; - var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) - CopyPackedValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); - } - } - - private static void Copy3TuplesWithMappingSlow(in FixedReadOnlyList3 sources, Tuple target, Pair[] map) - { - for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { - var sourceInfo = map[targetIndex]; - var sourceTupleIndex = sourceInfo.First; - var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) { - CopyValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); - } - } - } - - private static void Copy3TuplesWithMappingFast(in FixedReadOnlyList3 sources, PackedTuple target, Pair[] map) - { - for (int targetIndex = 0; targetIndex < map.Length; targetIndex++) { - var sourceInfo = map[targetIndex]; - var sourceTupleIndex = sourceInfo.First; - var sourceFieldIndex = sourceInfo.Second; - if (sourceTupleIndex >= 0 && sourceFieldIndex >= 0) { - CopyPackedValue(sources[sourceTupleIndex], sourceFieldIndex, target, targetIndex); - } - } - } - private static void MergeTuplesPreferOriginSlow(Tuple origin, Tuple difference, int startIndex, int length) { var bound = startIndex + length; From 5a0b213b9bfcf5f4abebc848b04bf0df75c2b859 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 19:20:07 -0800 Subject: [PATCH 28/64] Rename SingleSourceMapTransform class to MapTransform --- .../Tuples/Transform/MapTransformTest.cs | 2 +- Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs | 6 +++--- .../Linq/Materialization/MaterializationContext.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 4 ++-- .../Orm/Rse/Providers/Compilable/AggregateProvider.cs | 4 ++-- .../Orm/Rse/Providers/Compilable/CalculateProvider.cs | 4 ++-- .../Orm/Rse/Providers/Compilable/IncludeProvider.cs | 4 ++-- .../Orm/Rse/Providers/Compilable/OrderProviderBase.cs | 4 ++-- .../Orm/Rse/Providers/Compilable/RowNumberProvider.cs | 4 ++-- .../Orm/Rse/Transformation/RedundantColumnRemover.cs | 4 ++-- .../{MapTransformTuple1.cs => MapTransformTuple.cs} | 6 +++--- .../{SingleSourceMapTransform.cs => MapTransform.cs} | 11 +++-------- 12 files changed, 26 insertions(+), 31 deletions(-) rename Orm/Xtensive.Orm/Tuples/Transform/Internals/{MapTransformTuple1.cs => MapTransformTuple.cs} (90%) rename Orm/Xtensive.Orm/Tuples/Transform/{SingleSourceMapTransform.cs => MapTransform.cs} (88%) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs index ffee34da37..040c8e2fdb 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs @@ -17,7 +17,7 @@ public class MapTransformTest public void MainTest() { var source = Tuple.Create(1); - var transform = new SingleSourceMapTransform(true, TupleDescriptor.Create(), new[] {-1, 0}); + var transform = new MapTransform(true, TupleDescriptor.Create(), new[] {-1, 0}); var result = transform.Apply(TupleTransformType.TransformedTuple, source); Assert.AreEqual(TupleFieldState.Default, result.GetFieldState(0)); Assert.AreEqual(TupleFieldState.Available, result.GetFieldState(1)); diff --git a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs index bde10db25c..8f79b5a8fc 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs @@ -13,14 +13,14 @@ namespace Xtensive.Orm.Internals internal readonly struct TypeMapping { public readonly TypeInfo Type; - public readonly SingleSourceMapTransform KeyTransform; - public readonly SingleSourceMapTransform Transform; + public readonly MapTransform KeyTransform; + public readonly MapTransform Transform; public readonly IReadOnlyList KeyIndexes; // Constructors - public TypeMapping(TypeInfo type, SingleSourceMapTransform keyTransform, SingleSourceMapTransform transform, IReadOnlyList keyIndexes) + public TypeMapping(TypeInfo type, MapTransform keyTransform, MapTransform transform, IReadOnlyList keyIndexes) { Type = type; KeyTransform = keyTransform; diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs index a79b73c7c6..0909a1c341 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs @@ -93,8 +93,8 @@ public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int ArraySegment allIndexes = MaterializationHelper.CreateSingleSourceMap(descriptor.Count, typeColumnMap); ArraySegment keyIndexes = allIndexes.Slice(0, keyInfo.TupleDescriptor.Count); - var transform = new SingleSourceMapTransform(true, descriptor, allIndexes); - var keyTransform = new SingleSourceMapTransform(true, keyInfo.TupleDescriptor, keyIndexes); + var transform = new MapTransform(true, descriptor, allIndexes); + var keyTransform = new MapTransform(true, keyInfo.TupleDescriptor, keyIndexes); result = new TypeMapping(type, keyTransform, transform, keyIndexes); diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 5150f1c715..02661c21f3 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -341,7 +341,7 @@ public object TypeDiscriminatorValue { /// /// Gets the version tuple extractor. /// - public SingleSourceMapTransform VersionExtractor { get; private set; } + public MapTransform VersionExtractor { get; private set; } /// /// Gets a value indicating whether this instance has version fields. @@ -815,7 +815,7 @@ private void BuildVersionExtractor() var types = versionColumns.Select(c => c.ValueType).ToArray(versionColumnsCount); var map = versionColumns.Select(c => c.Field.MappingInfo.Offset).ToArray(versionColumnsCount); var versionTupleDescriptor = TupleDescriptor.Create(types); - VersionExtractor = new SingleSourceMapTransform(true, versionTupleDescriptor, map); + VersionExtractor = new MapTransform(true, versionTupleDescriptor, map); } private IDictionary, FieldInfo> BuildStructureFieldMapping() diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index dfaf4be6d9..e4f26b7c1f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -40,7 +40,7 @@ public sealed class AggregateProvider : UnaryProvider /// /// Gets header resize transform. /// - public SingleSourceMapTransform Transform { get; private set; } + public MapTransform Transform { get; private set; } /// protected override RecordSetHeader BuildHeader() @@ -81,7 +81,7 @@ protected override void Initialize() columnIndexes[i] = index; i++; } - Transform = new SingleSourceMapTransform(false, TupleDescriptor.Create(fieldTypes), columnIndexes); + Transform = new MapTransform(false, TupleDescriptor.Create(fieldTypes), columnIndexes); } /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index f88c21705e..22c8e64868 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -32,7 +32,7 @@ public class CalculateProvider : UnaryProvider, /// /// Gets header resize transform. /// - public SingleSourceMapTransform ResizeTransform { get; private set; } + public MapTransform ResizeTransform { get; private set; } /// @@ -54,7 +54,7 @@ protected override void Initialize() var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; - ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); + ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 839329d110..e774606a0f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -53,7 +53,7 @@ public sealed class IncludeProvider: UnaryProvider, /// public Expression>> FilterDataSource { get; private set; } - public SingleSourceMapTransform FilteredColumnsExtractionTransform { get; private set; } + public MapTransform FilteredColumnsExtractionTransform { get; private set; } public ConcatTransform ResultTransform { get; private set; } @@ -68,7 +68,7 @@ protected override RecordSetHeader BuildHeader() fieldTypes[index] = newHeader.Columns[FilteredColumns[index]].Type; } var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - FilteredColumnsExtractionTransform = new SingleSourceMapTransform(true, tupleDescriptor, FilteredColumns); + FilteredColumnsExtractionTransform = new MapTransform(true, tupleDescriptor, FilteredColumns); ResultTransform = new ConcatTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); return newHeader; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs index 603f2a7f08..b47123b259 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs @@ -33,7 +33,7 @@ public abstract class OrderProviderBase : UnaryProvider /// /// Gets the key extractor transform. /// - public SingleSourceMapTransform OrderKeyExtractorTransform { get; private set; } + public MapTransform OrderKeyExtractorTransform { get; private set; } /// /// Extracts the key part from using . @@ -82,7 +82,7 @@ protected override void Initialize() map[i] = p.Key; } var orderKeyDescriptor = TupleDescriptor.Create(fieldTypes); - OrderKeyExtractorTransform = new SingleSourceMapTransform(true, orderKeyDescriptor, map); + OrderKeyExtractorTransform = new MapTransform(true, orderKeyDescriptor, map); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index c7ac80acbd..82b6a75315 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -25,7 +25,7 @@ public sealed class RowNumberProvider : UnaryProvider /// /// Gets header resize transform. /// - public SingleSourceMapTransform ResizeTransform { get; private set; } + public MapTransform ResizeTransform { get; private set; } /// protected override void Initialize() @@ -34,7 +34,7 @@ protected override void Initialize() var columnIndexes = new int[Header.Length]; for (int i = 0; i < columnIndexes.Length; i++) columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; - ResizeTransform = new SingleSourceMapTransform(false, Header.TupleDescriptor, columnIndexes); + ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); } /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs index a232228af6..d973b2c636 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs @@ -41,13 +41,13 @@ protected override Provider VisitRaw(RawProvider provider) var mapping = mappings[provider]; if (mapping.SequenceEqual(Enumerable.Range(0, provider.Header.Length))) return provider; - var mappingTransform = new SingleSourceMapTransform(true, provider.Header.TupleDescriptor, mapping); + var mappingTransform = new MapTransform(true, provider.Header.TupleDescriptor, mapping); var newExpression = RemapRawProviderSource(provider.Source, mappingTransform); return new RawProvider(provider.Header.Select(mapping), newExpression); } private static Expression>> RemapRawProviderSource( - Expression>> source, SingleSourceMapTransform mappingTransform) + Expression>> source, MapTransform mappingTransform) { Func selector = tuple => mappingTransform.Apply(TupleTransformType.Auto, tuple); var newExpression = Expression.Call(SelectMethodInfo, source.Body, Expression.Constant(selector)); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs similarity index 90% rename from Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs rename to Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index 6ef4bb24d8..ce331c8d79 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -11,10 +11,10 @@ namespace Xtensive.Tuples.Transform.Internals { /// - /// A result tuple mapping 1 source tuple to a single one (this). + /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - internal sealed class MapTransformTuple1 : TransformedTuple + internal sealed class MapTransformTuple : TransformedTuple { private readonly Tuple source; private Tuple defaultResult; @@ -86,7 +86,7 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// /// The transform. /// Source tuple. - public MapTransformTuple1(SingleSourceMapTransform transform, Tuple source) + public MapTransformTuple(MapTransform transform, Tuple source) : base(transform) { this.source = source; diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs similarity index 88% rename from Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs rename to Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 5438a8fe39..4b35f8f755 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SingleSourceMapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -18,7 +18,7 @@ namespace Xtensive.Tuples.Transform /// Maps fields of a destination tuple to the specified fields of of the source tuple. /// [Serializable] - public class SingleSourceMapTransform : ITupleTransform + public class MapTransform : ITupleTransform { private IReadOnlyList map; @@ -33,11 +33,6 @@ public class SingleSourceMapTransform : ITupleTransform /// > public bool IsReadOnly { get; } - /// - /// Means that no mapping is available for the specified field index. - /// - public const int NoMapping = int.MinValue; - /// /// Gets or sets destination-to-source field map for the first source only. /// @@ -59,7 +54,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source) goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: - return new MapTransformTuple1(this, source); + return new MapTransformTuple(this, source); case TupleTransformType.Tuple: Tuple result = Tuple.Create(Descriptor); source.CopyTo(result, map); @@ -87,7 +82,7 @@ public override string ToString() /// property value. /// Initial property value. /// property value. - public SingleSourceMapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) + public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) { ArgumentValidator.EnsureArgumentNotNull(descriptor, nameof(descriptor)); ArgumentValidator.EnsureArgumentNotNull(map, nameof(map)); From 05a94e1f690d78849cacf5120da58db5de84edf7 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 20:49:52 -0800 Subject: [PATCH 29/64] Get rid of the TransformedTuple base class --- .../Tuples/Transform/ConcatTransform.cs | 2 +- .../Internals/ConcatTransformTuple.cs | 18 ++++++-- .../Transform/Internals/MapTransformTuple.cs | 20 ++++++--- .../Internals/SegmentTransformTuple.cs | 20 ++++++--- .../Tuples/Transform/MapTransform.cs | 2 +- .../Tuples/Transform/SegmentTransform.cs | 2 +- .../TransformedTuple{TTupleTransform}.cs | 45 ------------------- 7 files changed, 46 insertions(+), 63 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index be33e8777e..f407c6fb67 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -31,7 +31,7 @@ public sealed class ConcatTransform : ITupleTransform /// First transformation source. /// Second transformation source. /// Transformation result - - /// either or descendant, + /// either instance or the descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source1, Tuple source2) { return transformType switch { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs index d8e2dae7ad..ca29147f87 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs @@ -11,8 +11,9 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - internal sealed class ConcatTransformTuple : TransformedTuple + internal sealed class ConcatTransformTuple : Tuple, ITransformedTuple { + private readonly ConcatTransform transform; private readonly Tuple source1; private readonly Tuple source2; private readonly int totalCount; @@ -22,7 +23,10 @@ internal sealed class ConcatTransformTuple : TransformedTuple /// Gets the default result tuple. /// Can be used to get default values for the result tuple fields. /// - private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); + private Tuple DefaultResult => defaultResult ??= Tuple.Create(transform.Descriptor); + + /// + public override TupleDescriptor Descriptor => transform.Descriptor; #region GetFieldState, GetValue, SetValue methods @@ -54,7 +58,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TupleTransform.IsReadOnly) { + if (transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } var (source, index) = GetSourceAndFieldIndex(fieldIndex); @@ -72,10 +76,16 @@ public override void SetValue(int fieldIndex, object fieldValue) return source2Index < 0 ? (source1, fieldIndex) : (source2, source2Index); } + /// + public override string ToString() => + string.Format(Strings.TransformedTupleFormat, base.ToString(), transform, $"{source1}, {source2}"); + + // Constructors + public ConcatTransformTuple(ConcatTransform transform, Tuple source1, Tuple source2) - : base(transform) { + this.transform = transform; this.source1 = source1; this.source2 = source2; totalCount = source1.Count + source2.Count; diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs index ce331c8d79..9155f1684f 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -14,8 +14,9 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - internal sealed class MapTransformTuple : TransformedTuple + internal sealed class MapTransformTuple : Tuple, ITransformedTuple { + private readonly MapTransform transform; private readonly Tuple source; private Tuple defaultResult; @@ -23,7 +24,10 @@ internal sealed class MapTransformTuple : TransformedTuple /// Gets the default result tuple. /// Can be used to get default values for the result tuple fields. /// - private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); + private Tuple DefaultResult => defaultResult ??= Tuple.Create(transform.Descriptor); + + /// + public override TupleDescriptor Descriptor => transform.Descriptor; #region GetFieldState, GetValue, SetValue methods @@ -55,7 +59,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TupleTransform.IsReadOnly) { + if (transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); @@ -65,19 +69,23 @@ public override void SetValue(int fieldIndex, object fieldValue) private int GetSourceFieldIndex(int fieldIndex) { - var mappedIndex = TupleTransform.Map[fieldIndex]; + var mappedIndex = transform.Map[fieldIndex]; return mappedIndex < 0 ? TransformUtil.NoMapping : mappedIndex; } protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && TupleTransform.IsReadOnly) { + if (isWriting && transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } var index = GetSourceFieldIndex(fieldIndex); return index == TransformUtil.NoMapping ? default : source.GetMappedContainer(index, isWriting); } + /// + public override string ToString() => + string.Format(Strings.TransformedTupleFormat, base.ToString(), transform, source); + // Constructors @@ -87,8 +95,8 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// The transform. /// Source tuple. public MapTransformTuple(MapTransform transform, Tuple source) - : base(transform) { + this.transform = transform; this.source = source; } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs index 258e5b858d..1aad9129e8 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs @@ -11,8 +11,9 @@ namespace Xtensive.Tuples.Transform.Internals /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - internal sealed class SegmentTransformTuple : TransformedTuple + internal sealed class SegmentTransformTuple : Tuple, ITransformedTuple { + private readonly SegmentTransform transform; private readonly Tuple source; private Tuple defaultResult; @@ -20,7 +21,10 @@ internal sealed class SegmentTransformTuple : TransformedTuple /// Gets the default result tuple. /// Can be used to get default values for the result tuple fields. /// - private Tuple DefaultResult => defaultResult ??= Tuple.Create(TupleTransform.Descriptor); + private Tuple DefaultResult => defaultResult ??= Tuple.Create(transform.Descriptor); + + /// + public override TupleDescriptor Descriptor => transform.Descriptor; #region GetFieldState, GetValue, SetValue methods @@ -52,7 +56,7 @@ public override object GetValue(int fieldIndex, out TupleFieldState fieldState) /// public override void SetValue(int fieldIndex, object fieldValue) { - if (TupleTransform.IsReadOnly) { + if (transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); } source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); @@ -62,14 +66,20 @@ public override void SetValue(int fieldIndex, object fieldValue) private int GetSourceFieldIndex(int fieldIndex) { - var sourceIndex = TupleTransform.Segment.Offset + fieldIndex; + var sourceIndex = transform.Segment.Offset + fieldIndex; return sourceIndex < 0 || sourceIndex >= source.Count ? TransformUtil.NoMapping : sourceIndex; } + /// + public override string ToString() => + string.Format(Strings.TransformedTupleFormat, base.ToString(), transform, source); + + // Constructors + public SegmentTransformTuple(SegmentTransform transform, Tuple source) - : base(transform) { + this.transform = transform; this.source = source; } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 4b35f8f755..3d4c2f0e96 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -44,7 +44,7 @@ public class MapTransform : ITupleTransform /// The type of transformation to perform. /// Transformation source. /// Transformation result - - /// either or descendant, + /// either instance or the descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 466ae95198..3779bc717c 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -38,7 +38,7 @@ public ref readonly Segment Segment /// The type of transformation to perform. /// Transformation source. /// Transformation result - - /// either or descendant, + /// either instance or the descendant, /// dependently on specified . public Tuple Apply(TupleTransformType transformType, Tuple source) { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs deleted file mode 100644 index 8b14fc2eef..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexey Kochetov -// Created: 2008.04.30 - -using System; -using System.Diagnostics; - - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for all transformed tuples. - /// - [Serializable] - public abstract class TransformedTuple : Tuple, ITransformedTuple - where TTupleTransform : ITupleTransform - { - /// - /// Gets or sets the transform used to produce this instance. - /// - public TTupleTransform TupleTransform { get; } - - /// - public override TupleDescriptor Descriptor => TupleTransform.Descriptor; - - /// - public override string ToString() - { - return string.Format(Strings.TransformedTupleFormat, base.ToString(), TupleTransform, string.Empty); - } - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Tuple transform. - protected TransformedTuple(TTupleTransform transform) - { - TupleTransform = transform; - } - } -} \ No newline at end of file From 486dfd4e8de352dd8e4181feb1947f92f9ce057c Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Tue, 28 Dec 2021 21:09:58 -0800 Subject: [PATCH 30/64] Get rid of the redundand ITupleTransform interface --- .../Tuples/Transform/ConcatTransform.cs | 8 +++--- .../Tuples/Transform/ITupleTransform.cs | 25 ------------------- .../Tuples/Transform/MapTransform.cs | 6 ++--- .../Tuples/Transform/SegmentTransform.cs | 6 ++--- 4 files changed, 10 insertions(+), 35 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index f407c6fb67..110914363e 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform /// This class is used for concatenation of two s. /// [Serializable] - public sealed class ConcatTransform : ITupleTransform + public sealed class ConcatTransform { private readonly (TupleDescriptor first, TupleDescriptor second) sources; @@ -65,9 +65,9 @@ public override string ToString() /// /// Initializes a new instance of this type. /// - /// property value. - /// First tuple descriptor to combine. - /// Second tuple descriptor to combine. + /// Indicates whethere the transformed is read only. + /// The of the first source . + /// The of the second source . public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) { var (firstCount, secondCount) = (first.Count, second.Count); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs deleted file mode 100644 index 1e3f073322..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/ITupleTransform.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2008-2021 Xtensive LLC. -// This code is distributed under MIT license terms. -// See the License.txt file in the project root for more information. -// Created by: Alexey Kochetov -// Created: 2008.04.30 - -namespace Xtensive.Tuples.Transform -{ - /// - /// Interface implemented by any of the tuple transforms. - /// - public interface ITupleTransform - { - /// - /// Gets describing the tuples - /// this transform may produce. - /// - TupleDescriptor Descriptor { get; } - - /// - /// Indicates whether transform always produces read-only tuples or not. - /// - bool IsReadOnly { get; } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 3d4c2f0e96..0b5515c826 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -18,7 +18,7 @@ namespace Xtensive.Tuples.Transform /// Maps fields of a destination tuple to the specified fields of of the source tuple. /// [Serializable] - public class MapTransform : ITupleTransform + public class MapTransform { private IReadOnlyList map; @@ -79,8 +79,8 @@ public override string ToString() /// /// Initializes a new instance of this type. /// - /// property value. - /// Initial property value. + /// Indicates whethere the transformed is read only. + /// The of the target . /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 3779bc717c..29cff4b419 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform /// /// Extracts specified from the . /// - public sealed class SegmentTransform : ITupleTransform + public sealed class SegmentTransform// : ITupleTransform { private readonly Segment segment; @@ -72,8 +72,8 @@ public override string ToString() /// /// Initializes a new instance of this type. /// - /// property value. - /// Source tuple descriptor. + /// Indicates whethere the transformed is read only. + /// The of the source . /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) { From d66ccaf25b4448514b63614839e59c6346dbc893 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 11:04:22 -0800 Subject: [PATCH 31/64] Add argument checks to the ConcatTransform and SegmentTransform constructors --- Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs | 4 ++++ Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index 110914363e..05a038e7eb 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -5,6 +5,7 @@ // Created: 2008.04.30 using System; +using Xtensive.Core; using Xtensive.Reflection; using Xtensive.Tuples.Transform.Internals; @@ -70,6 +71,9 @@ public override string ToString() /// The of the second source . public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) { + ArgumentValidator.EnsureArgumentNotNull(first, nameof(first)); + ArgumentValidator.EnsureArgumentNotNull(second, nameof(second)); + var (firstCount, secondCount) = (first.Count, second.Count); var types = new Type[firstCount + secondCount]; Array.Copy(first.FieldTypes, types, firstCount); diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 29cff4b419..5fde215826 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -77,6 +77,8 @@ public override string ToString() /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) { + ArgumentValidator.EnsureArgumentNotNull(sourceDescriptor, nameof(sourceDescriptor)); + IsReadOnly = isReadOnly; var fields = new ArraySegment(sourceDescriptor.FieldTypes, segment.Offset, segment.Length); From de8275331786cecc7c9345d55a19cc92e0202b74 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 11:07:12 -0800 Subject: [PATCH 32/64] Remove redundand comment --- Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 5fde215826..eba914a5ba 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -14,7 +14,7 @@ namespace Xtensive.Tuples.Transform /// /// Extracts specified from the . /// - public sealed class SegmentTransform// : ITupleTransform + public sealed class SegmentTransform { private readonly Segment segment; From a0f01beab8444ec3fd59eca6df61d44e05571068 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 11:08:09 -0800 Subject: [PATCH 33/64] Change MapTransform.ToString implementation --- Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 0b5515c826..1327a7c8c2 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -67,7 +67,7 @@ public Tuple Apply(TupleTransformType transformType, Tuple source) /// public override string ToString() { - string description = $"1: {map.ToCommaDelimitedString()}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + string description = $"[{map.ToCommaDelimitedString()}], {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; return string.Format(Strings.TupleTransformFormat, GetType().GetShortName(), description); From 9542f44c210a07dd63e9440c37611c8b771d2e68 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 12:15:38 -0800 Subject: [PATCH 34/64] Remove excessive MapTransform usage in the CalculateProvider --- .../Providers/Compilable/CalculateProvider.cs | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index 22c8e64868..132beacbaf 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -7,9 +7,6 @@ using System; using Xtensive.Core; -using Xtensive.Tuples.Transform; -using Xtensive.Collections; - namespace Xtensive.Orm.Rse.Providers { /// @@ -22,18 +19,12 @@ public class CalculateProvider : UnaryProvider, /// /// Gets a value indicating whether calculated columns should be inlined. /// - public bool IsInlined { get; private set; } + public bool IsInlined { get; } /// /// Gets the calculated columns. /// - public CalculatedColumn[] CalculatedColumns { get; private set; } - - /// - /// Gets header resize transform. - /// - public MapTransform ResizeTransform { get; private set; } - + public CalculatedColumn[] CalculatedColumns { get; } /// protected override RecordSetHeader BuildHeader() @@ -47,16 +38,6 @@ protected override string ParametersToString() return CalculatedColumns.ToCommaDelimitedString(); } - /// - protected override void Initialize() - { - base.Initialize(); - var columnIndexes = new int[Header.Length]; - for (int i = 0; i < columnIndexes.Length; i++) - columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); - } - // Constructors @@ -80,10 +61,10 @@ public CalculateProvider(CompilableProvider source, bool isInlined, params Calcu : base(ProviderType.Calculate, source) { IsInlined = isInlined; + var sourceHeaderLength = Source.Header.Length; var columns = new CalculatedColumn[columnDescriptors.Length]; for (int i = 0; i < columnDescriptors.Length; i++) { - var col = new CalculatedColumn(columnDescriptors[i], Source.Header.Length + i); - columns.SetValue(col, i); + columns[i] = new CalculatedColumn(columnDescriptors[i], sourceHeaderLength + i); } CalculatedColumns = columns; Initialize(); From e2c24ed3108edd6266e55def7adc05d9223eb19c Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 12:20:46 -0800 Subject: [PATCH 35/64] Remove excessive MapTransform usage in the RowNumberProvider --- .../Providers/Compilable/RowNumberProvider.cs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 82b6a75315..06bc766299 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -1,13 +1,11 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// Copyright (C) 2009-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Gamzov // Created: 2009.03.05 using System; -using Xtensive.Collections; using Xtensive.Reflection; -using Xtensive.Tuples.Transform; namespace Xtensive.Orm.Rse.Providers { @@ -22,21 +20,6 @@ public sealed class RowNumberProvider : UnaryProvider /// public SystemColumn SystemColumn { get; private set; } - /// - /// Gets header resize transform. - /// - public MapTransform ResizeTransform { get; private set; } - - /// - protected override void Initialize() - { - base.Initialize(); - var columnIndexes = new int[Header.Length]; - for (int i = 0; i < columnIndexes.Length; i++) - columnIndexes[i] = (i < Source.Header.Length) ? i : TransformUtil.NoMapping; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); - } - /// protected override RecordSetHeader BuildHeader() { From 6ff76ffd25a13a9a6ce7cf412b1c224162c79afc Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 12:26:25 -0800 Subject: [PATCH 36/64] Remove excessive MapTransform usage in the AggregateProvider --- .../Providers/Compilable/AggregateProvider.cs | 30 ++----------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index e4f26b7c1f..8b10282d14 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -5,15 +5,9 @@ // Created: 2008.09.18 using System; -using System.Collections.Generic; -using System.Linq; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Reflection; -using Xtensive.Tuples; -using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Tuples.Transform; namespace Xtensive.Orm.Rse.Providers { @@ -30,17 +24,12 @@ public sealed class AggregateProvider : UnaryProvider /// /// Gets the aggregate columns. /// - public AggregateColumn[] AggregateColumns { get; private set; } + public AggregateColumn[] AggregateColumns { get; } /// /// Gets column indexes to group by. /// - public int[] GroupColumnIndexes { get; private set; } - - /// - /// Gets header resize transform. - /// - public MapTransform Transform { get; private set; } + public int[] GroupColumnIndexes { get; } /// protected override RecordSetHeader BuildHeader() @@ -69,21 +58,6 @@ protected override string ParametersToString() GroupColumnIndexes.ToCommaDelimitedString()); } - /// - protected override void Initialize() - { - base.Initialize(); - var fieldTypes = new Type[GroupColumnIndexes.Length]; - var columnIndexes = new int[GroupColumnIndexes.Length]; - var i = 0; - foreach (var index in GroupColumnIndexes) { - fieldTypes[i] = Source.Header.Columns[index].Type; - columnIndexes[i] = index; - i++; - } - Transform = new MapTransform(false, TupleDescriptor.Create(fieldTypes), columnIndexes); - } - /// /// Gets the type of the aggregate column according to a and original column type. /// From 64276ac562a74e2bbc857de9300cc1f95f0a7ebc Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 12:32:44 -0800 Subject: [PATCH 37/64] Remove excessive MapTransform usage in the OrderProviderBase --- .../Providers/Compilable/OrderProviderBase.cs | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs index b47123b259..7c79b25c38 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs @@ -5,17 +5,9 @@ // Created: 2008.07.08 using System; -using System.Collections.Generic; -using System.Globalization; using System.Linq; using Xtensive.Collections; -using Xtensive.Comparison; using Xtensive.Core; -using Xtensive.Orm.FullTextSearchCondition.Nodes; -using Xtensive.Tuples; -using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Tuples.Transform; -using Xtensive.Orm.Rse.Providers; namespace Xtensive.Orm.Rse.Providers { @@ -30,21 +22,6 @@ public abstract class OrderProviderBase : UnaryProvider /// public DirectionCollection Order { get; private set; } - /// - /// Gets the key extractor transform. - /// - public MapTransform OrderKeyExtractorTransform { get; private set; } - - /// - /// Extracts the key part from using . - /// - /// The tuple to extract the key from. - /// A tuple containing extracted order key. - public Tuple OrderKeyExtractor(Tuple tuple) - { - return OrderKeyExtractorTransform.Apply(TupleTransformType.Auto, tuple); - } - /// protected override RecordSetHeader BuildHeader() { @@ -59,32 +36,6 @@ protected override string ParametersToString() .ToCommaDelimitedString(); } - /// - protected override void Initialize() - { - base.Initialize(); - var comparisonRules = new ComparisonRules[Order.Count]; - for (int i = 0; i < Order.Count; i++) { - var orderItem = Order[i]; - var culture = CultureInfo.InvariantCulture; - var column = Header.Columns[orderItem.Key]; - var mappedColumn = column as MappedColumn; - if (mappedColumn != null && mappedColumn.ColumnInfoRef != null) - culture = mappedColumn.ColumnInfoRef.CultureInfo; - comparisonRules[i] = new ComparisonRule(orderItem.Value, culture); - } - - var fieldTypes = new Type[Order.Count]; - var map = new int[Order.Count]; - for (var i = 0; i < Order.Count; i++) { - var p = Order[i]; - fieldTypes[i] = Header.Columns[p.Key].Type; - map[i] = p.Key; - } - var orderKeyDescriptor = TupleDescriptor.Create(fieldTypes); - OrderKeyExtractorTransform = new MapTransform(true, orderKeyDescriptor, map); - } - // Constructors From f80234992a120aaf91aafc3b2328990377d9048d Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 12:48:20 -0800 Subject: [PATCH 38/64] Remove excessive MapTransform and ConcatTransform usage from the IncludeProvider --- .../Orm/Providers/SqlCompiler.Include.cs | 4 ++-- .../Providers/Compilable/IncludeProvider.cs | 20 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs index 10d388a018..b03e256f93 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs @@ -77,7 +77,7 @@ protected SqlExpression CreateIncludeViaComplexConditionExpression( IncludeProvider provider, Func valueAccessor, IList sourceColumns, out QueryParameterBinding binding) { - var filterTupleDescriptor = provider.FilteredColumnsExtractionTransform.Descriptor; + var filterTupleDescriptor = provider.FilteredTupleDescriptor; var mappings = filterTupleDescriptor.Select(type => Driver.GetTypeMapping(type)); binding = new QueryRowFilterParameterBinding(mappings, valueAccessor); var resultExpression = SqlDml.DynamicFilter(binding); @@ -89,7 +89,7 @@ protected SqlExpression CreateIncludeViaTemporaryTableExpression( IncludeProvider provider, IList sourceColumns, out TemporaryTableDescriptor tableDescriptor) { - var filterTupleDescriptor = provider.FilteredColumnsExtractionTransform.Descriptor; + var filterTupleDescriptor = provider.FilteredTupleDescriptor; var filteredColumns = provider.FilteredColumns.Select(index => sourceColumns[index]).ToList(); tableDescriptor = DomainHandler.TemporaryTableManager .BuildDescriptor(Mapping, Guid.NewGuid().ToString(), filterTupleDescriptor); diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index e774606a0f..d713b00f50 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -13,7 +13,6 @@ using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; using Xtensive.Tuples.Transform; -using System.Linq; using Xtensive.Reflection; namespace Xtensive.Orm.Rse.Providers @@ -53,27 +52,26 @@ public sealed class IncludeProvider: UnaryProvider, /// public Expression>> FilterDataSource { get; private set; } - public MapTransform FilteredColumnsExtractionTransform { get; private set; } - - public ConcatTransform ResultTransform { get; private set; } + public TupleDescriptor FilteredTupleDescriptor { get; private set; } private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); /// protected override RecordSetHeader BuildHeader() { - var newHeader = Source.Header.Add(new SystemColumn(ResultColumnName, 0, WellKnownTypes.Bool)); + return Source.Header.Add(new SystemColumn(ResultColumnName, 0, WellKnownTypes.Bool)); + } + + protected override void Initialize() + { + base.Initialize(); var fieldTypes = new Type[FilteredColumns.Count]; for (var index = 0; index < fieldTypes.Length; index++) { - fieldTypes[index] = newHeader.Columns[FilteredColumns[index]].Type; + fieldTypes[index] = Header.Columns[FilteredColumns[index]].Type; } - var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - FilteredColumnsExtractionTransform = new MapTransform(true, tupleDescriptor, FilteredColumns); - ResultTransform = new ConcatTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); - return newHeader; + FilteredTupleDescriptor = TupleDescriptor.Create(fieldTypes); } - // Constructors /// From 8c5fcef0d6a13e1a8c293dfd78eb2860741c31b6 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 29 Dec 2021 18:13:48 -0800 Subject: [PATCH 39/64] Explicitly turn off connection encription for the testing purposes --- Orm/Xtensive.Orm.Tests.Framework/Orm.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.Tests.Framework/Orm.config b/Orm/Xtensive.Orm.Tests.Framework/Orm.config index cad6f65b54..ea90d576db 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/Orm.config +++ b/Orm/Xtensive.Orm.Tests.Framework/Orm.config @@ -90,7 +90,7 @@ + connectionString="User ID=dotest;Password=dotest;Data Source=localhost;Initial Catalog=DO-Tests;MultipleActiveResultSets=True;Encrypt=False" /> From 98d418afc1c0ea6f7fc6ccdbcbe3a33310d544b3 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Mon, 10 Jan 2022 22:30:34 -0800 Subject: [PATCH 40/64] Clean dead usings in the files related to the Xtensive.Tuples.Transform namespace --- .../Tuples/Transform/SegmentTransformTest.cs | 3 --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 1 - Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs | 4 ---- Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs | 1 - .../Orm/Internals/Prefetch/ReferencedEntityContainer.cs | 1 - .../Orm/Linq/Materialization/MaterializationContext.cs | 1 - Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 3 --- .../Orm/Rse/Providers/Compilable/IncludeProvider.cs | 1 - Orm/Xtensive.Orm/Orm/VersionValidator.cs | 1 - 9 files changed, 16 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/SegmentTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/SegmentTransformTest.cs index 3ff89f7945..bb2335d607 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/SegmentTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/SegmentTransformTest.cs @@ -8,10 +8,7 @@ using NUnit.Framework; using Xtensive.Comparison; using Xtensive.Core; -using Xtensive.Tuples; -using Xtensive.Orm.Tests; using Xtensive.Tuples.Transform; -using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Tests.Core.Tuples.Transform { diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index a12755895c..c4b87a979b 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -10,7 +10,6 @@ using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Internals; using Xtensive.Orm.Model; diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs index 0449ab413e..35b89507cc 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs @@ -5,12 +5,8 @@ // Created: 2009.08.04 using System; -using Xtensive.Tuples; -using Xtensive.Orm.Providers; using Xtensive.Orm.Rse.Providers; using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Tuples.Transform; -using Xtensive.Orm.Rse; namespace Xtensive.Orm.Internals { diff --git a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs index 5499d5184d..dc10cd6f82 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs @@ -4,7 +4,6 @@ // Created by: Alexey Kulakov // Created: 2014.05.06 -using System; using System.Linq; using Xtensive.Tuples.Transform; diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs index b5f6305c02..c47eb67648 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs @@ -10,7 +10,6 @@ using Xtensive.Core; using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Tuples.Transform; using Xtensive.Orm.Model; diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs index 0909a1c341..3ec9d69760 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Xtensive.Core; using Xtensive.Tuples.Transform; using Xtensive.Orm.Internals; diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index bf260ed48f..5aa1c217c4 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -5,16 +5,13 @@ // Created: 2007.09.10 using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Diagnostics; using System.Reflection; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Validation; using Xtensive.Reflection; -using Xtensive.Sorting; using Tuple = Xtensive.Tuples.Tuple; using Xtensive.Tuples.Transform; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index d713b00f50..bf23a586a4 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -12,7 +12,6 @@ using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Tuples.Transform; using Xtensive.Reflection; namespace Xtensive.Orm.Rse.Providers diff --git a/Orm/Xtensive.Orm/Orm/VersionValidator.cs b/Orm/Xtensive.Orm/Orm/VersionValidator.cs index 3692027b13..aa26bf9f43 100644 --- a/Orm/Xtensive.Orm/Orm/VersionValidator.cs +++ b/Orm/Xtensive.Orm/Orm/VersionValidator.cs @@ -10,7 +10,6 @@ using Xtensive.Core; using Xtensive.Orm.Logging; -using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; using Xtensive.Tuples.Transform; using Xtensive.Orm.Internals; From 435d1d3b13bf41cb6df193f80acf467f1e462b24 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Fri, 21 Jan 2022 16:18:49 -0800 Subject: [PATCH 41/64] Get rid of virtual method calls in the Provider descendant constructors --- Directory.Build.props | 2 +- .../Orm/Providers/SqlIncludeProvider.cs | 1 - Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs | 17 ----- .../Orm/Providers/SqlStoreProvider.cs | 1 - .../Providers/Compilable/AggregateProvider.cs | 33 +++++----- .../Rse/Providers/Compilable/AliasProvider.cs | 11 +--- .../Rse/Providers/Compilable/ApplyProvider.cs | 24 +++---- .../Providers/Compilable/BinaryProvider.cs | 31 ++++++---- .../Providers/Compilable/CalculateProvider.cs | 29 ++++----- .../Providers/Compilable/ConcatProvider.cs | 53 ++++++++-------- .../Compilable/ContainsTableProvider.cs | 62 ++++++++----------- .../Providers/Compilable/DistinctProvider.cs | 1 - .../Providers/Compilable/ExceptProvider.cs | 33 +++++----- .../Providers/Compilable/ExistenceProvider.cs | 21 +++---- .../Providers/Compilable/FilterProvider.cs | 2 - .../Providers/Compilable/FreeTextProvider.cs | 52 +++++++--------- .../Providers/Compilable/IncludeProvider.cs | 62 +++++++------------ .../Rse/Providers/Compilable/IndexProvider.cs | 16 +---- .../Providers/Compilable/IntersectProvider.cs | 35 +++++------ .../Rse/Providers/Compilable/JoinProvider.cs | 48 +++++++------- .../Rse/Providers/Compilable/LockProvider.cs | 12 ++-- .../Providers/Compilable/OrderProviderBase.cs | 10 +-- .../Providers/Compilable/PagingProvider.cs | 12 ++-- .../Compilable/PredicateJoinProvider.cs | 11 +--- .../Rse/Providers/Compilable/RawProvider.cs | 14 +---- .../Providers/Compilable/RowNumberProvider.cs | 17 +++-- .../Rse/Providers/Compilable/SeekProvider.cs | 19 ++---- .../Providers/Compilable/SelectProvider.cs | 10 +-- .../Rse/Providers/Compilable/SkipProvider.cs | 17 +++-- .../Rse/Providers/Compilable/SortProvider.cs | 1 - .../Rse/Providers/Compilable/StoreProvider.cs | 30 ++------- .../Rse/Providers/Compilable/TagProvider.cs | 3 +- .../Rse/Providers/Compilable/TakeProvider.cs | 17 +++-- .../Rse/Providers/Compilable/UnaryProvider.cs | 22 ++++--- .../Rse/Providers/Compilable/UnionProvider.cs | 42 +++++++------ .../Rse/Providers/Compilable/VoidProvider.cs | 10 +-- .../Orm/Rse/Providers/CompilableProvider.cs | 4 +- .../Orm/Rse/Providers/ExecutableProvider.cs | 4 +- .../Orm/Rse/Providers/Provider.cs | 32 ++-------- 39 files changed, 319 insertions(+), 502 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9f6115910c..79b83e1174 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -82,7 +82,7 @@ true snupkg true - true + false diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs index c42a30cc15..d30eb90394 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs @@ -98,7 +98,6 @@ public SqlIncludeProvider( : base(handlers, request, tableDescriptor, origin, new []{source}) { this.filterDataSource = filterDataSource; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs index 0962682d79..53acdb3b61 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs @@ -92,23 +92,6 @@ public SqlProvider(HandlerAccessor handlers, QueryRequest request, { this.handlers = handlers; Request = request; - if (typeof (SqlProvider)==GetType()) - Initialize(); - } - - /// - /// Initializes a new instance of this class. - /// - /// The provider. - /// The permanent reference. - public SqlProvider(SqlProvider provider, SqlTable permanentReference) - : base(provider.Origin, provider.Sources.Cast().ToArray()) - { - this.permanentReference = permanentReference; - handlers = provider.handlers; - Request = provider.Request; - if (typeof (SqlProvider)==GetType()) - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs index 5c33958d1d..10e720d5d4 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs @@ -52,7 +52,6 @@ public SqlStoreProvider( StoreProvider origin, ExecutableProvider source) : base(handlers, request, descriptor, origin, new[] {source}) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index 8b10282d14..fefbc3d800 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -31,14 +31,6 @@ public sealed class AggregateProvider : UnaryProvider /// public int[] GroupColumnIndexes { get; } - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header - .Select(GroupColumnIndexes) - .Add(AggregateColumns); - } - /// protected override string ParametersToString() { @@ -160,6 +152,22 @@ private static NotSupportedException AggregateNotSupported(Type sourceColumnType #endregion + private static RecordSetHeader BuildHeaderAndColumns( + CompilableProvider source, ref int[] groupIndexes, AggregateColumnDescriptor[] columnDescriptors, out AggregateColumn[] aggregateColumns) + { + groupIndexes ??= Array.Empty(); + aggregateColumns = new AggregateColumn[columnDescriptors.Length]; + var sourceHeader = source.Header; + var sourceHeaderColumns = sourceHeader.Columns; + for (int i = 0; i < columnDescriptors.Length; i++) { + AggregateColumnDescriptor descriptor = columnDescriptors[i]; + var type = GetAggregateColumnType(sourceHeaderColumns[descriptor.SourceIndex].Type, descriptor.AggregateType); + aggregateColumns[i] = new AggregateColumn(descriptor, groupIndexes.Length + i, type); + } + + return sourceHeader.Select(groupIndexes).Add(aggregateColumns); + } + // Constructors /// @@ -169,18 +177,11 @@ private static NotSupportedException AggregateNotSupported(Type sourceColumnType /// The descriptors of . /// The column indexes to group by. public AggregateProvider(CompilableProvider source, int[] groupIndexes, params AggregateColumnDescriptor[] columnDescriptors) - : base(ProviderType.Aggregate, source) + : base(ProviderType.Aggregate, BuildHeaderAndColumns(source, ref groupIndexes, columnDescriptors, out var columns), source) { groupIndexes = groupIndexes ?? Array.Empty(); - var columns = new AggregateColumn[columnDescriptors.Length]; - for (int i = 0; i < columnDescriptors.Length; i++) { - AggregateColumnDescriptor descriptor = columnDescriptors[i]; - var type = GetAggregateColumnType(Source.Header.Columns[descriptor.SourceIndex].Type, descriptor.AggregateType); - columns[i] = new AggregateColumn(descriptor, groupIndexes.Length + i, type); - } AggregateColumns = columns; GroupColumnIndexes = groupIndexes; - Initialize(); } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AliasProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AliasProvider.cs index 46eee42e6a..7a6abca373 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AliasProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AliasProvider.cs @@ -20,13 +20,7 @@ public sealed class AliasProvider : UnaryProvider /// /// Alias of the result. /// - public string Alias { get; private set; } - - /// - protected override RecordSetHeader BuildHeader() - { - return base.BuildHeader().Alias(Alias); - } + public string Alias { get; } /// protected override string ParametersToString() @@ -43,10 +37,9 @@ protected override string ParametersToString() /// The property value. /// The property value. public AliasProvider(CompilableProvider source, string alias) - : base(ProviderType.Alias, source) + : base(ProviderType.Alias, source.Header.Alias(alias), source) { Alias = alias; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ApplyProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ApplyProvider.cs index 6dfaf0f40f..3a9c1f98dc 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ApplyProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ApplyProvider.cs @@ -24,34 +24,22 @@ public sealed class ApplyProvider : BinaryProvider, /// /// Gets the apply parameter. /// - public ApplyParameter ApplyParameter { get; private set; } + public ApplyParameter ApplyParameter { get; } /// /// Gets a value indicating whether columns of this provider should be inlined. /// - public bool IsInlined { get; private set; } + public bool IsInlined { get; } /// /// Gets apply type. /// - public JoinType ApplyType { get; private set; } + public JoinType ApplyType { get; } /// /// Gets a value indicating whether applying of single or first row expected. /// - public ApplySequenceType SequenceType { get; private set;} - - /// - protected override RecordSetHeader BuildHeader() - { - switch (ApplyType) { - case JoinType.Inner: - case JoinType.LeftOuter: - return base.BuildHeader(); - default: - throw new ArgumentOutOfRangeException(); - } - } + public ApplySequenceType SequenceType { get; } /// protected override string ParametersToString() @@ -77,11 +65,13 @@ public ApplyProvider(ApplyParameter applyParameter, CompilableProvider left, Com public ApplyProvider(ApplyParameter applyParameter, CompilableProvider left, CompilableProvider right, bool isInlined, ApplySequenceType applySequenceType, JoinType applyType) : base(ProviderType.Apply, left, right) { + if (applyType is not JoinType.Inner and not JoinType.LeftOuter) { + throw new ArgumentOutOfRangeException(nameof(applyType)); + } ApplyParameter = applyParameter; IsInlined = isInlined; SequenceType = applySequenceType; ApplyType = applyType; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs index c209d1e6cb..dc9d19d080 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs @@ -20,18 +20,12 @@ public abstract class BinaryProvider : CompilableProvider /// /// Left source. /// - public CompilableProvider Left { get; private set; } + public CompilableProvider Left { get; } /// /// Right source. /// - public CompilableProvider Right { get; private set; } - - /// - protected override RecordSetHeader BuildHeader() - { - return Left.Header.Join(Right.Header); - } + public CompilableProvider Right { get; } // Constructors @@ -41,12 +35,25 @@ protected override RecordSetHeader BuildHeader() /// /// The type of provider. /// The provider. - /// The provider. + /// The provider. protected BinaryProvider(ProviderType type, CompilableProvider left, CompilableProvider right) - : base(type, left, right) + : this(type, left.Header.Join(right.Header), left, right) + { + } + + /// + /// Initializes a new instance of this class. + /// + /// The type of provider. + /// The header of the resulting record set. + /// The provider. + /// The provider. + protected BinaryProvider(ProviderType type, RecordSetHeader header, CompilableProvider left, CompilableProvider right) + : base(type, header, left, right) { - ArgumentValidator.EnsureArgumentNotNull(left, "left"); - ArgumentValidator.EnsureArgumentNotNull(right, "right"); + ArgumentValidator.EnsureArgumentNotNull(header, nameof(header)); + ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); + ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); Left = left; Right = right; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index 132beacbaf..ecb6458526 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -26,12 +26,6 @@ public class CalculateProvider : UnaryProvider, /// public CalculatedColumn[] CalculatedColumns { get; } - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header.Add(CalculatedColumns); - } - /// protected override string ParametersToString() { @@ -41,6 +35,19 @@ protected override string ParametersToString() // Constructors + private static RecordSetHeader BuildHeaderAndColumns( + CompilableProvider source, CalculatedColumnDescriptor[] columnDescriptors, out CalculatedColumn[] calculatedColumns) + { + var sourceHeader = source.Header; + var sourceHeaderLength = sourceHeader.Length; + calculatedColumns = new CalculatedColumn[columnDescriptors.Length]; + for (int i = 0; i < columnDescriptors.Length; i++) { + calculatedColumns[i] = new CalculatedColumn(columnDescriptors[i], sourceHeaderLength + i); + } + + return sourceHeader.Add(calculatedColumns); + } + /// /// Initializes a new instance of this class. /// @@ -58,16 +65,10 @@ public CalculateProvider(CompilableProvider source, params CalculatedColumnDescr /// The property value. /// The descriptors of . public CalculateProvider(CompilableProvider source, bool isInlined, params CalculatedColumnDescriptor[] columnDescriptors) - : base(ProviderType.Calculate, source) + : base(ProviderType.Calculate, BuildHeaderAndColumns(source, columnDescriptors, out var calculatedColumns), source) { IsInlined = isInlined; - var sourceHeaderLength = Source.Header.Length; - var columns = new CalculatedColumn[columnDescriptors.Length]; - for (int i = 0; i < columnDescriptors.Length; i++) { - columns[i] = new CalculatedColumn(columnDescriptors[i], sourceHeaderLength + i); - } - CalculatedColumns = columns; - Initialize(); + CalculatedColumns = calculatedColumns; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs index 4e445f39d4..508044fcee 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs @@ -6,9 +6,6 @@ using System; using System.Collections.Generic; -using Xtensive.Collections; - - using System.Linq; namespace Xtensive.Orm.Rse.Providers @@ -20,58 +17,60 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ConcatProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() + + + // Constructors + + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - EnsureConcatIsPossible(); + var leftHeader = left.Header; + var rightHeader = right.Header; + EnsureConcatIsPossible(leftHeader, rightHeader); var mappedColumnIndexes = new List(); var columns = new List(); - for (int i = 0; i < Left.Header.Columns.Count; i++) { - var leftColumn = Left.Header.Columns[i]; - var rightColumn = Right.Header.Columns[i]; - if (leftColumn is MappedColumn && rightColumn is MappedColumn) { - var leftMappedColumn = (MappedColumn) leftColumn; - var rightMappedColumn = (MappedColumn) rightColumn; + for (int i = 0; i < leftHeader.Columns.Count; i++) { + var leftColumn = leftHeader.Columns[i]; + var rightColumn = rightHeader.Columns[i]; + if (leftColumn is MappedColumn leftMappedColumn && rightColumn is MappedColumn rightMappedColumn) { if (leftMappedColumn.ColumnInfoRef.Equals(rightMappedColumn.ColumnInfoRef)) { columns.Add(leftMappedColumn); mappedColumnIndexes.Add(i); - } - else + } + else { columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); + } } - else + else { columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); + } } - var columnGroups = Left.Header.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); + var columnGroups = leftHeader.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); return new RecordSetHeader( - Left.Header.TupleDescriptor, - columns, + leftHeader.TupleDescriptor, + columns, columnGroups, null, null); } - /// Something went wrong. - private void EnsureConcatIsPossible() + private static void EnsureConcatIsPossible(RecordSetHeader leftHeader, RecordSetHeader rightHeader) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (!left.Equals(right)) + var left = leftHeader.TupleDescriptor; + var right = rightHeader.TupleDescriptor; + if (!left.Equals(right)) { throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Concatenation")); + } } - - // Constructors - /// /// Initializes a new instance of this class. /// /// The left provider to intersect. /// The right provider to intersect. public ConcatProvider(CompilableProvider left, CompilableProvider right) - : base(ProviderType.Concat, left, right) + : base(ProviderType.Concat, BuildHeader(left, right), left, right) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs index 9fce18d70e..ea13416abb 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Model; using Xtensive.Reflection; @@ -19,49 +18,26 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ContainsTableProvider : CompilableProvider { - private readonly RecordSetHeader indexHeader; + public Func SearchCriteria { get; } - public Func SearchCriteria { get; private set; } + public IndexInfoRef PrimaryIndex { get; } - public IndexInfoRef PrimaryIndex { get; private set; } + public bool FullFeatured { get; } - public bool FullFeatured { get; private set; } + public Func TopN { get; } - public Func TopN { get; private set; } + public IReadOnlyList TargetColumns { get; } - public IReadOnlyList TargetColumns { get; private set; } - protected override RecordSetHeader BuildHeader() - { - return indexHeader; - } + // Constructors - public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, bool fullFeatured) - : this(index, searchCriteria, rankColumnName, new List(), null, fullFeatured) + private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { - } - - public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, bool fullFeatured) - : this(index, searchCriteria, rankColumnName, targetColumns, null, fullFeatured) - { - - } - - public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, Func topNByRank, bool fullFeatured) - : base(ProviderType.ContainsTable) - { - SearchCriteria = searchCriteria; - FullFeatured = fullFeatured; - PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); - TargetColumns = targetColumns.Select(tc => index.Columns.First(c => c.Column == tc)) - .ToList(targetColumns.Count) - .AsReadOnly(); - TopN = topNByRank; - if (FullFeatured) { + if (fullFeatured) { var primaryIndexRecordsetHeader = index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); - indexHeader = primaryIndexRecordsetHeader.Add(rankColumn); + return primaryIndexRecordsetHeader.Add(rankColumn); } else { var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; @@ -75,9 +51,25 @@ public ContainsTableProvider(FullTextIndexInfo index, Func (Column) new MappedColumn("KEY", i, c.Key.ValueType)) .Append(new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double)); - indexHeader = new RecordSetHeader(tupleDescriptor, columns); + return new RecordSetHeader(tupleDescriptor, columns); } - Initialize(); + } + + public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, bool fullFeatured) + : this(index, searchCriteria, rankColumnName, targetColumns, null, fullFeatured) + { + } + + public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, Func topNByRank, bool fullFeatured) + : base(ProviderType.ContainsTable, BuildHeader(index, rankColumnName, fullFeatured)) + { + SearchCriteria = searchCriteria; + FullFeatured = fullFeatured; + PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); + TargetColumns = targetColumns.Select(tc => index.Columns.First(c => c.Column == tc)) + .ToList(targetColumns.Count) + .AsReadOnly(); + TopN = topNByRank; } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/DistinctProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/DistinctProvider.cs index f8fa7fbb38..9f23130099 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/DistinctProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/DistinctProvider.cs @@ -24,7 +24,6 @@ public sealed class DistinctProvider : UnaryProvider public DistinctProvider(CompilableProvider source) : base(ProviderType.Distinct, source) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs index 455b8f3a31..3c7ec80f4f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs @@ -5,10 +5,7 @@ // Created: 2009.04.01 using System; -using System.Diagnostics; -using Xtensive.Collections; - - +using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers { @@ -19,32 +16,32 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ExceptProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() - { - EnsureIntersectIsPossible(); - return Left.Header; - } - private void EnsureIntersectIsPossible() + + // Constructors + + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (left!=right) + ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); + ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); + + var leftHeader = left.Header; + var leftDescriptor = leftHeader.TupleDescriptor; + var rightDescriptor = right.Header.TupleDescriptor; + if (leftDescriptor != rightDescriptor) { throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Except operation")); + } + return leftHeader; } - - // Constructors - /// /// Initializes a new instance of this class. /// /// The left provider to execute except operation. /// The right provider to to execute except operation. public ExceptProvider(CompilableProvider left, CompilableProvider right) - : base(ProviderType.Except, left, right) + : base(ProviderType.Except, BuildHeader(left, right), left, right) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs index b279960563..e02a4f152e 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs @@ -5,11 +5,8 @@ // Created: 2009.03.20 using System; -using System.Diagnostics; -using Xtensive.Collections; using Xtensive.Reflection; using Xtensive.Tuples; -using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Rse.Providers { @@ -23,28 +20,26 @@ public sealed class ExistenceProvider : UnaryProvider /// /// Gets the name of the existence column. /// - public string ExistenceColumnName { get; private set; } + public string ExistenceColumnName { get; } private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); - /// - protected override RecordSetHeader BuildHeader() - { - return new RecordSetHeader( - BoolTupleDescriptor, new[] { new SystemColumn(ExistenceColumnName, 0, WellKnownTypes.Bool) }); - } - // Constructors + private static RecordSetHeader BuildHeader(string existenceColumnName) + { + return new RecordSetHeader( + BoolTupleDescriptor, new[] { new SystemColumn(existenceColumnName, 0, WellKnownTypes.Bool) }); + } + /// /// Initializes a new instance of this class. /// public ExistenceProvider(CompilableProvider source, string existenceColumnName) - : base(ProviderType.Existence, source) + : base(ProviderType.Existence, BuildHeader(existenceColumnName), source) { ExistenceColumnName = existenceColumnName; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs index dae6ee6d27..cb5fde3cfe 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs @@ -8,7 +8,6 @@ using System.Linq.Expressions; using Xtensive.Core; -using Xtensive.Linq; using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; @@ -57,7 +56,6 @@ public FilterProvider(CompilableProvider source, Expression> p : base(ProviderType.Filter, source) { Predicate = predicate; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs index fcd5ce5c68..6737d2093d 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs @@ -5,14 +5,11 @@ // Created: 2009.12.28 using System; -using System.Diagnostics; using System.Linq; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Model; using Xtensive.Reflection; using Xtensive.Tuples; -using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Rse.Providers @@ -23,38 +20,23 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class FreeTextProvider : CompilableProvider { - private readonly RecordSetHeader indexHeader; + public Func SearchCriteria { get; } - public Func SearchCriteria { get; private set; } + public Func TopN { get; } - public Func TopN { get; private set; } + public IndexInfoRef PrimaryIndex { get; } - public IndexInfoRef PrimaryIndex { get; private set; } + public bool FullFeatured { get; } - public bool FullFeatured { get; private set; } - protected override RecordSetHeader BuildHeader() - { - return indexHeader; - } - - public FreeTextProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, bool fullFeatured) - : this(index, searchCriteria, rankColumnName, null, fullFeatured) - { - } + // Constructors - public FreeTextProvider( - FullTextIndexInfo index, Func searchCriteria, string rankColumnName, Func topN, bool fullFeatured) - : base(ProviderType.FreeText) + private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { - SearchCriteria = searchCriteria; - FullFeatured = fullFeatured; - TopN = topN; - PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); - if (FullFeatured) { + if (fullFeatured) { var primaryIndexRecordsetHeader = index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); - indexHeader = primaryIndexRecordsetHeader.Add(rankColumn); + return primaryIndexRecordsetHeader.Add(rankColumn); } else { var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; @@ -68,9 +50,23 @@ public FreeTextProvider( var columns = primaryIndexKeyColumns .Select((c, i) => (Column) new MappedColumn("KEY", i, c.Key.ValueType)) .Append(new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double)); - indexHeader = new RecordSetHeader(tupleDescriptor, columns); + return new RecordSetHeader(tupleDescriptor, columns); } - Initialize(); + } + + public FreeTextProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, bool fullFeatured) + : this(index, searchCriteria, rankColumnName, null, fullFeatured) + { + } + + public FreeTextProvider( + FullTextIndexInfo index, Func searchCriteria, string rankColumnName, Func topN, bool fullFeatured) + : base(ProviderType.FreeText, BuildHeader(index, rankColumnName, fullFeatured)) + { + SearchCriteria = searchCriteria; + FullFeatured = fullFeatured; + TopN = topN; + PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index bf23a586a4..a213727c29 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -9,10 +9,9 @@ using System.Diagnostics; using System.Linq.Expressions; using Xtensive.Core; - +using Xtensive.Reflection; using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Reflection; namespace Xtensive.Orm.Rse.Providers { @@ -28,18 +27,18 @@ public sealed class IncludeProvider: UnaryProvider, /// /// Gets a value indicating whether result column should be inlined. /// - public bool IsInlined { get; private set; } + public bool IsInlined { get; } /// /// Gets the name of the column. /// - public string ResultColumnName { get; private set; } + public string ResultColumnName { get; } /// /// Gets the algorithm that performs filtering. /// For non-SQL storages value of this field has no effect. /// - public IncludeAlgorithm Algorithm { get; private set; } + public IncludeAlgorithm Algorithm { get; } /// /// Gets the filtered columns. @@ -49,30 +48,30 @@ public sealed class IncludeProvider: UnaryProvider, /// /// Gets filter data. /// - public Expression>> FilterDataSource { get; private set; } + public Expression>> FilterDataSource { get; } - public TupleDescriptor FilteredTupleDescriptor { get; private set; } + public TupleDescriptor FilteredTupleDescriptor { get; } - private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header.Add(new SystemColumn(ResultColumnName, 0, WellKnownTypes.Bool)); - } + // Constructors - protected override void Initialize() + private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( + CompilableProvider source, IReadOnlyList filteredColumns, string resultColumnName, out TupleDescriptor filteredTupleDescriptor) { - base.Initialize(); - var fieldTypes = new Type[FilteredColumns.Count]; - for (var index = 0; index < fieldTypes.Length; index++) { - fieldTypes[index] = Header.Columns[FilteredColumns[index]].Type; + ArgumentValidator.EnsureArgumentNotNull(source, nameof(source)); + ArgumentValidator.EnsureArgumentNotNull(filteredColumns, nameof(filteredColumns)); + ArgumentValidator.EnsureArgumentNotNullOrEmpty(resultColumnName, nameof(resultColumnName)); + + var header = source.Header.Add(new SystemColumn(resultColumnName, 0, WellKnownTypes.Bool)); + var columnCount = filteredColumns.Count; + var fieldTypes = new Type[columnCount]; + for (var index = 0; index < columnCount; index++) { + fieldTypes[index] = header.Columns[filteredColumns[index]].Type; } - FilteredTupleDescriptor = TupleDescriptor.Create(fieldTypes); + filteredTupleDescriptor = TupleDescriptor.Create(fieldTypes); + return header; } - // Constructors - /// /// Initializes a new instance of this class. /// @@ -84,29 +83,16 @@ protected override void Initialize() /// A value for . public IncludeProvider(CompilableProvider source, IncludeAlgorithm algorithm, bool isInlined, Expression>> filterDataSource, string resultColumnName, IReadOnlyList filteredColumns) - : base(ProviderType.Include, source) + : base(ProviderType.Include, BuildHeaderAndFilteredTupleDescriptor(source, filteredColumns, resultColumnName, out var filteredTupleDescriptor), source) { - ArgumentValidator.EnsureArgumentNotNull(filterDataSource, "filterDataSource"); - ArgumentValidator.EnsureArgumentNotNullOrEmpty(resultColumnName, "resultColumnName"); - ArgumentValidator.EnsureArgumentNotNull(filteredColumns, "filteredColumns"); + ArgumentValidator.EnsureArgumentNotNull(filterDataSource, nameof(FilterDataSource)); Algorithm = algorithm; IsInlined = isInlined; FilterDataSource = filterDataSource; ResultColumnName = resultColumnName; - switch (filteredColumns) { - case int[] columnArray: - FilteredColumns = Array.AsReadOnly(columnArray); - break; - case List columnList: - FilteredColumns = columnList.AsReadOnly(); - break; - default: - FilteredColumns = filteredColumns; - break; - } - - Initialize(); + FilteredColumns = filteredColumns; + FilteredTupleDescriptor = filteredTupleDescriptor; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IndexProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IndexProvider.cs index 9c3dee4177..5756c8614a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IndexProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IndexProvider.cs @@ -5,9 +5,7 @@ // Created: 2008.07.03 using System; -using Xtensive.Collections; using Xtensive.Orm.Model; -using Xtensive.Orm.Rse.Compilation; using IndexInfo = Xtensive.Orm.Model.IndexInfo; namespace Xtensive.Orm.Rse.Providers @@ -18,18 +16,10 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class IndexProvider : CompilableProvider { - private readonly RecordSetHeader indexHeader; - /// /// Reference to the instance within the domain. /// - public IndexInfoRef Index { get; private set; } - - /// - protected override RecordSetHeader BuildHeader() - { - return indexHeader; - } + public IndexInfoRef Index { get; } /// protected override string ParametersToString() @@ -41,11 +31,9 @@ protected override string ParametersToString() // Constructors public IndexProvider(IndexInfo index) - : base(ProviderType.Index) + : base(ProviderType.Index, index.GetRecordSetHeader()) { - indexHeader = index.GetRecordSetHeader(); Index = new IndexInfoRef(index); - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs index 540e26ccce..9f4ae1a47e 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs @@ -5,13 +5,10 @@ // Created: 2009.04.01 using System; -using System.Diagnostics; -using Xtensive.Collections; - - +using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers -{ +{ /// /// Produces intersect operation between and /// sources. @@ -19,32 +16,32 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class IntersectProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() - { - EnsureIntersectIsPossible(); - return Left.Header; - } - private void EnsureIntersectIsPossible() + + // Constructors + + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (left!=right) + ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); + ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); + + var leftHeader = left.Header; + var leftDescriptor = leftHeader.TupleDescriptor; + var rightDescriptor = right.Header.TupleDescriptor; + if (leftDescriptor != rightDescriptor) { throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Intersection")); + } + return leftHeader; } - - // Constructors - /// /// Initializes a new instance of this class. /// /// The left provider to intersect. /// The right provider to intersect. public IntersectProvider(CompilableProvider left, CompilableProvider right) - : base(ProviderType.Intersect, left, right) + : base(ProviderType.Intersect, BuildHeader(left, right), left, right) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/JoinProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/JoinProvider.cs index 0d389d3e10..6d7e99f7cc 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/JoinProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/JoinProvider.cs @@ -5,13 +5,10 @@ // Created: 2008.07.03 using System; -using System.Collections.Generic; using System.Linq; -using Xtensive.Collections; using Xtensive.Core; - namespace Xtensive.Orm.Rse.Providers { /// @@ -26,17 +23,17 @@ public sealed class JoinProvider : BinaryProvider /// /// Join operation type. /// - public JoinType JoinType { get; private set; } + public JoinType JoinType { get; } /// /// Pairs of equal column indexes. /// - public Pair[] EqualIndexes { get; private set; } + public Pair[] EqualIndexes { get; } /// /// Pairs of equal columns. /// - public Pair[] EqualColumns { get; private set; } + public Pair[] EqualColumns { get; } /// protected override string ParametersToString() @@ -46,21 +43,21 @@ protected override string ParametersToString() EqualColumns.Select(p => p.First.Name + " == " + p.Second.Name).ToCommaDelimitedString()); } - /// - protected override void Initialize() + + // Constructors + + private static Pair[] BuildEqualColumns(ColumnCollection leftHeaderColumns, ColumnCollection rightHeaderColumns, Pair[] equalIndexes) { - base.Initialize(); - EqualColumns = new Pair[EqualIndexes.Length]; - for (int i = 0; i < EqualIndexes.Length; i++) - EqualColumns[i] = new Pair( - Left.Header.Columns[EqualIndexes[i].First], - Right.Header.Columns[EqualIndexes[i].Second] + var equalColumns = new Pair[equalIndexes.Length]; + for (int i = 0; i < equalIndexes.Length; i++) { + equalColumns[i] = new Pair( + leftHeaderColumns[equalIndexes[i].First], + rightHeaderColumns[equalIndexes[i].Second] ); + } + return equalColumns; } - - // Constructors - /// /// Initializes a new instance of this class. /// @@ -72,12 +69,13 @@ protected override void Initialize() public JoinProvider(CompilableProvider left, CompilableProvider right, JoinType joinType, params Pair[] equalIndexes) : base(ProviderType.Join, left, right) { - if (equalIndexes==null || equalIndexes.Length==0) + if (equalIndexes == null || equalIndexes.Length == 0) { throw new ArgumentException( - Strings.ExAtLeastOneColumnIndexPairMustBeSpecified, "equalIndexes"); + Strings.ExAtLeastOneColumnIndexPairMustBeSpecified, nameof(equalIndexes)); + } JoinType = joinType; EqualIndexes = equalIndexes; - Initialize(); + EqualColumns = BuildEqualColumns(left.Header.Columns, right.Header.Columns, equalIndexes); } /// @@ -91,15 +89,17 @@ public JoinProvider(CompilableProvider left, CompilableProvider right, JoinType public JoinProvider(CompilableProvider left, CompilableProvider right, JoinType joinType, params int[] equalIndexes) : base(ProviderType.Join, left, right) { - if (equalIndexes==null || equalIndexes.Length<2) + if (equalIndexes == null || equalIndexes.Length < 2) { throw new ArgumentException( - Strings.ExAtLeastOneColumnIndexPairMustBeSpecified, "equalIndexes"); + Strings.ExAtLeastOneColumnIndexPairMustBeSpecified, nameof(equalIndexes)); + } var ei = new Pair[equalIndexes.Length / 2]; - for (int i = 0, j = 0; i < ei.Length; i++) + for (int i = 0, j = 0; i < ei.Length; i++) { ei[i] = new Pair(equalIndexes[j++], equalIndexes[j++]); + } JoinType = joinType; EqualIndexes = ei; - Initialize(); + EqualColumns = BuildEqualColumns(left.Header.Columns, right.Header.Columns, ei); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs index 17d7bcb97c..b3831a7d07 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs @@ -34,12 +34,9 @@ public sealed class LockProvider : UnaryProvider /// The property value. /// The mode of the lock to be acquired. /// The behavior of the lock. - public LockProvider(CompilableProvider source, LockMode lockMode, LockBehavior lockBehavior) : - base(ProviderType.Lock, source) + public LockProvider(CompilableProvider source, LockMode lockMode, LockBehavior lockBehavior) + : this(source, () => lockMode, () => lockBehavior) { - LockMode = () => lockMode; - LockBehavior = () => lockBehavior; - Initialize(); } /// @@ -48,12 +45,11 @@ public LockProvider(CompilableProvider source, LockMode lockMode, LockBehavior l /// The property value. /// The delegate returning the mode of the lock to be acquired. /// The delegate returning the behavior of the lock. - public LockProvider(CompilableProvider source, Func lockMode, Func lockBehavior) : - base(ProviderType.Lock, source) + public LockProvider(CompilableProvider source, Func lockMode, Func lockBehavior) + : base(ProviderType.Lock, source) { LockMode = lockMode; LockBehavior = lockBehavior; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs index 7c79b25c38..0355a94f15 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/OrderProviderBase.cs @@ -20,13 +20,7 @@ public abstract class OrderProviderBase : UnaryProvider /// /// Sort order of the index. /// - public DirectionCollection Order { get; private set; } - - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header.Sort(Order); - } + public DirectionCollection Order { get; } /// protected override string ParametersToString() @@ -46,7 +40,7 @@ protected override string ParametersToString() /// The property value. /// The property value. protected OrderProviderBase(ProviderType providerType, CompilableProvider source, DirectionCollection order) - : base(providerType, source) + : base(providerType, source.Header.Sort(order), source) { Order = order; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PagingProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PagingProvider.cs index d917092ab7..fee3766124 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PagingProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PagingProvider.cs @@ -5,7 +5,6 @@ // Created: 2011.03.24 using System; -using System.Diagnostics; using Xtensive.Core; @@ -20,22 +19,22 @@ public sealed class PagingProvider : UnaryProvider /// /// From number function. /// - public Func From { get; private set; } + public Func From { get; } /// /// To number function. /// - public Func To { get; private set; } + public Func To { get; } /// /// Skip amount function. /// - public Func Skip { get; private set; } + public Func Skip { get; } /// /// Take amount function. /// - public Func Take { get; private set; } + public Func Take { get; } /// protected override string ParametersToString() @@ -59,7 +58,6 @@ public PagingProvider(CompilableProvider provider, Func s Take = take; From = context => skip(context) + 1; To = context => take(context) + skip(context); - Initialize(); } /// @@ -75,7 +73,6 @@ public PagingProvider(CompilableProvider provider, int skip, int take) Take = context => take; From = context => skip + 1; To = context => take + skip; - Initialize(); } /// @@ -90,7 +87,6 @@ public PagingProvider(CompilableProvider provider, PagingProvider pagingProvider Take = pagingProvider.Take; From = pagingProvider.From; To = pagingProvider.To; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PredicateJoinProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PredicateJoinProvider.cs index 2b24c1c3ee..5559cc2b4f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PredicateJoinProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/PredicateJoinProvider.cs @@ -5,14 +5,8 @@ // Created: 2009.03.05 using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Linq.Expressions; -using Xtensive.Collections; -using Xtensive.Core; -using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Rse.Providers @@ -27,12 +21,12 @@ public sealed class PredicateJoinProvider : BinaryProvider /// /// Join operation type. /// - public JoinType JoinType { get; private set; } + public JoinType JoinType { get; } /// /// Gets the predicate. /// - public Expression> Predicate { get; private set; } + public Expression> Predicate { get; } // Constructors @@ -46,7 +40,6 @@ public PredicateJoinProvider(CompilableProvider left, CompilableProvider right, { Predicate = predicate; JoinType = joinType; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs index 60a89bd075..26fbdf182a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs @@ -7,13 +7,10 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using Xtensive.Collections; using Xtensive.Core; -using Xtensive.Linq; using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; -using Xtensive.Orm.Rse.Compilation; namespace Xtensive.Orm.Rse.Providers { @@ -23,7 +20,6 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class RawProvider : CompilableProvider { - private readonly RecordSetHeader header; private Func> compiledSource; /// @@ -42,12 +38,6 @@ public Func> CompiledSource { } } - /// - protected override RecordSetHeader BuildHeader() - { - return header; - } - /// protected override string ParametersToString() { @@ -63,11 +53,9 @@ protected override string ParametersToString() /// The property value. /// The property value. public RawProvider(RecordSetHeader header, Expression>> source) - : base(ProviderType.Raw) + : base(ProviderType.Raw, header) { Source = source; - this.header = header; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 06bc766299..5936b62253 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -20,25 +20,24 @@ public sealed class RowNumberProvider : UnaryProvider /// public SystemColumn SystemColumn { get; private set; } - /// - protected override RecordSetHeader BuildHeader() + // Constructors + + private static RecordSetHeader CreateHeaderAndColumn(CompilableProvider source, string columnName, out SystemColumn systemColumn) { - return Source.Header.Add(SystemColumn); + var sourceHeader = source.Header; + systemColumn = new SystemColumn(columnName, sourceHeader.Length, WellKnownTypes.Int64); + return sourceHeader.Add(systemColumn); } - - // Constructors - /// /// Initializes a new instance of this class. /// /// The property value. /// The name of . public RowNumberProvider(CompilableProvider source, string columnName) - : base(ProviderType.RowNumber, source) + : base(ProviderType.RowNumber, CreateHeaderAndColumn(source, columnName, out var systemColumn), source) { - SystemColumn = new SystemColumn(columnName, Source.Header.Length, WellKnownTypes.Int64); - Initialize(); + SystemColumn = systemColumn; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs index 1b9c1a34d9..dbd2814e51 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs @@ -5,11 +5,7 @@ // Created: 2008.08.14 using System; -using System.Linq.Expressions; using Xtensive.Core; - -using Xtensive.Linq; -using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Rse.Providers @@ -38,24 +34,21 @@ protected override string ParametersToString() /// Initializes a new instance of this class. /// /// The property value. - /// The property value. - public SeekProvider(CompilableProvider source, Func key) - : base(ProviderType.Seek, source) + /// Wrapped to property value. + public SeekProvider(CompilableProvider source, Tuple key) + : this(source, context => key) { - Key = key; - Initialize(); } /// /// Initializes a new instance of this class. /// /// The property value. - /// Wrapped to property value. - public SeekProvider(CompilableProvider source, Tuple key) + /// The property value. + public SeekProvider(CompilableProvider source, Func key) : base(ProviderType.Seek, source) { - Key = context => key; - Initialize(); + Key = key; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs index 181e0054d4..2fe33bde2a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs @@ -23,12 +23,6 @@ public sealed class SelectProvider : UnaryProvider /// public IReadOnlyList ColumnIndexes { [DebuggerStepThrough] get; } - /// - protected override RecordSetHeader BuildHeader() - { - return base.BuildHeader().Select(ColumnIndexes); - } - /// protected override string ParametersToString() { @@ -42,7 +36,7 @@ protected override string ParametersToString() /// Initializes a new instance of this class. /// public SelectProvider(CompilableProvider provider, IReadOnlyList columnIndexes) - : base(ProviderType.Select, provider) + : base(ProviderType.Select, provider.Header.Select(columnIndexes), provider) { switch (columnIndexes) { case int[] indexArray: @@ -55,8 +49,6 @@ public SelectProvider(CompilableProvider provider, IReadOnlyList columnInde ColumnIndexes = columnIndexes; break; } - - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SkipProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SkipProvider.cs index 24a7a98d1e..4efdd3de3d 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SkipProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SkipProvider.cs @@ -18,7 +18,7 @@ public sealed class SkipProvider : UnaryProvider /// /// Skip amount function. /// - public Func Count { get; private set; } + public Func Count { get; } /// protected override string ParametersToString() @@ -33,24 +33,21 @@ protected override string ParametersToString() /// Initializes a new instance of this class. /// /// The property value. - /// The property value. - public SkipProvider(CompilableProvider provider, Func count) - : base(ProviderType.Skip, provider) + /// The value for function property. + public SkipProvider(CompilableProvider provider, int count) + : this(provider, context => count) { - Count = count; - Initialize(); } /// /// Initializes a new instance of this class. /// /// The property value. - /// The value for function property. - public SkipProvider(CompilableProvider provider, int count) + /// The property value. + public SkipProvider(CompilableProvider provider, Func count) : base(ProviderType.Skip, provider) { - Count = context => count; - Initialize(); + Count = count; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SortProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SortProvider.cs index 101bed0199..279ed35f36 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SortProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SortProvider.cs @@ -26,7 +26,6 @@ public sealed class SortProvider : OrderProviderBase public SortProvider(CompilableProvider source, DirectionCollection order) : base(ProviderType.Sort, source, order) { - Initialize(); } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs index 6a1d7b0ec3..69a545f345 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs @@ -16,23 +16,15 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class StoreProvider : CompilableProvider { - private readonly RecordSetHeader header; - /// /// Gets the name of saved data. /// - public string Name { get; private set; } + public string Name { get; } /// /// Source provider. /// - public Provider Source { get; private set; } - - /// - protected override RecordSetHeader BuildHeader() - { - return header; - } + public Provider Source { get; } /// protected override string ParametersToString() @@ -49,16 +41,12 @@ protected override string ParametersToString() /// The property value. /// The property value. public StoreProvider(RecordSetHeader header, string name) - : base (ProviderType.Store) + : base (ProviderType.Store, header) { ArgumentValidator.EnsureArgumentNotNull(header, "header"); ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, "name"); Name = name; - - this.header = header; - - Initialize(); } /// @@ -67,17 +55,13 @@ public StoreProvider(RecordSetHeader header, string name) /// The property value. /// The property value. public StoreProvider(Provider source, string name) - : base(ProviderType.Store, source) + : base(ProviderType.Store, source.Header, source) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, "name"); Name = name; Source = source; - - header = source.Header; - - Initialize(); } /// @@ -85,16 +69,12 @@ public StoreProvider(Provider source, string name) /// /// The property value. public StoreProvider(Provider source) - : base(ProviderType.Store, source) + : base(ProviderType.Store, source.Header, source) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); Name = Guid.NewGuid().ToString(); Source = source; - - header = source.Header; - - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs index 059fa632bf..5e443e5b75 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs @@ -23,9 +23,8 @@ public sealed class TagProvider : UnaryProvider public TagProvider(CompilableProvider source, string tag) : base(ProviderType.Tag, source) { - ArgumentValidator.EnsureArgumentNotNullOrEmptyOrWhiteSpace(tag, "tag"); + ArgumentValidator.EnsureArgumentNotNullOrEmptyOrWhiteSpace(tag, nameof(tag)); Tag = tag; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TakeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TakeProvider.cs index cddccbdf8c..ae45afe3cc 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TakeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TakeProvider.cs @@ -18,7 +18,7 @@ public sealed class TakeProvider : UnaryProvider /// /// Take amount function. /// - public Func Count { get; private set; } + public Func Count { get; } /// protected override string ParametersToString() @@ -33,24 +33,21 @@ protected override string ParametersToString() /// Initializes a new instance of this class. /// /// The property value. - /// The property value. - public TakeProvider(CompilableProvider provider, Func count) - : base(ProviderType.Take, provider) + /// The value for function property. + public TakeProvider(CompilableProvider provider, int count) + : this(provider, context => count) { - Count = count; - Initialize(); } /// /// Initializes a new instance of this class. /// /// The property value. - /// The value for function property. - public TakeProvider(CompilableProvider provider, int count) + /// The property value. + public TakeProvider(CompilableProvider provider, Func count) : base(ProviderType.Take, provider) { - Count = context => count; - Initialize(); + Count = count; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs index 9a91549a38..334cbbc356 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs @@ -4,7 +4,6 @@ // Created by: Alexey Kochetov // Created: 2008.07.22 -using Xtensive.Collections; using Xtensive.Core; @@ -18,13 +17,8 @@ public abstract class UnaryProvider : CompilableProvider /// /// Source provider. /// - public CompilableProvider Source { get; private set; } + public CompilableProvider Source { get; } - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header; - } // Constructors @@ -34,10 +28,22 @@ protected override RecordSetHeader BuildHeader() /// The type of the provider. /// The property value. protected UnaryProvider(ProviderType type, CompilableProvider source) - : base(type, source) + : this(type, source.Header, source) + { + } + + /// + /// Initializes a new instance of this class. + /// + /// The type of the provider. + /// The header of the produced sequence of records. + /// The property value. + protected UnaryProvider(ProviderType type, RecordSetHeader header, CompilableProvider source) + : base(type, header, source) { ArgumentValidator.EnsureArgumentNotNull(source, "source"); Source = source; } + } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs index 9b48469b24..e3f4373a3d 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs @@ -6,10 +6,8 @@ using System; using System.Collections.Generic; -using Xtensive.Collections; - - using System.Linq; +using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers { @@ -20,14 +18,22 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class UnionProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() + + // Constructors + + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - EnsureUnionIsPossible(); + ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); + ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); + + var leftHeader = left.Header; + var rightHeader = right.Header; + EnsureUnionIsPossible(leftHeader, rightHeader); var mappedColumnIndexes = new List(); var columns = new List(); - for (int i = 0; i < Left.Header.Columns.Count; i++) { - var leftColumn = Left.Header.Columns[i]; - var rightColumn = Right.Header.Columns[i]; + for (int i = 0; i < leftHeader.Columns.Count; i++) { + var leftColumn = leftHeader.Columns[i]; + var rightColumn = rightHeader.Columns[i]; if (leftColumn is MappedColumn && rightColumn is MappedColumn) { var leftMappedColumn = (MappedColumn) leftColumn; var rightMappedColumn = (MappedColumn) rightColumn; @@ -41,37 +47,33 @@ protected override RecordSetHeader BuildHeader() else columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); } - var columnGroups = Left.Header.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); + var columnGroups = leftHeader.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); return new RecordSetHeader( - Left.Header.TupleDescriptor, + leftHeader.TupleDescriptor, columns, columnGroups, null, null); } - /// InvalidOperationException. - private void EnsureUnionIsPossible() + private static void EnsureUnionIsPossible(RecordSetHeader leftHeader, RecordSetHeader rightHeader) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (!left.Equals(right)) + var left = leftHeader.TupleDescriptor; + var right = rightHeader.TupleDescriptor; + if (!left.Equals(right)) { throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Union operation")); + } } - - // Constructors - /// /// Initializes a new instance of this class. /// /// The left provider for union. /// The right provider for union. public UnionProvider(CompilableProvider left, CompilableProvider right) - : base(ProviderType.Union, left, right) + : base(ProviderType.Union, BuildHeader(left, right), left, right) { - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs index 84997c8683..538429f0ac 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs @@ -10,19 +10,11 @@ namespace Xtensive.Orm.Rse.Providers { public sealed class VoidProvider : CompilableProvider { - private readonly RecordSetHeader header; - - protected override RecordSetHeader BuildHeader() - { - return header; - } public VoidProvider(RecordSetHeader header) - : base(ProviderType.Void) + : base(ProviderType.Void, header) { ArgumentValidator.EnsureArgumentNotNull(header, "header"); - this.header = header; - Initialize(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs index 7e45c17828..7006d68733 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs @@ -18,8 +18,8 @@ public abstract class CompilableProvider : Provider // Constructors /// - protected CompilableProvider(ProviderType type, params Provider[] sources) - : base(type, sources) + protected CompilableProvider(ProviderType type, RecordSetHeader header, params Provider[] sources) + : base(type, header, sources) { } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs index 75f58628af..e9dcd87ef0 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs @@ -25,8 +25,6 @@ public abstract class ExecutableProvider : Provider /// public CompilableProvider Origin { get; private set; } - /// is . - protected override RecordSetHeader BuildHeader() => Origin.Header; #region OnXxxEnumerate methods (to override) @@ -159,7 +157,7 @@ public async Task GetRecordSetReaderAsync( /// The property value. /// The property value. protected ExecutableProvider(CompilableProvider origin, params ExecutableProvider[] sources) - : base(origin.Type, sources) + : base(origin.Type, origin.Header, sources) { Origin = origin; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs index 9d4aa1db99..243295d9db 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs @@ -24,7 +24,6 @@ public abstract class Provider private const int ToString_IndentSize = 2; private Provider[] sources; - private RecordSetHeader header; /// /// Gets of the current instance. @@ -49,37 +48,12 @@ private set { /// /// Gets or sets the header of the record sequence this provide produces. /// - public RecordSetHeader Header - { - [DebuggerStepThrough] - get { return header; } - [DebuggerStepThrough] - private set { - if (header!=null) - throw Exceptions.AlreadyInitialized("Header"); - header = value; - } - } - - /// - /// Builds the . - /// This method is invoked just once on each provider. - /// - /// Newly created to assign to property. - protected abstract RecordSetHeader BuildHeader(); + public RecordSetHeader Header { get; } private string DebuggerDisplayName { get { return GetType().GetShortName(); } } - /// - /// Performs initialization of the provider. - /// - protected virtual void Initialize() - { - Header = BuildHeader(); - } - #region ToString method /// @@ -135,10 +109,12 @@ protected virtual string ParametersToString() /// Initializes a new instance of this class. /// /// The type of the provider. + /// The header of the produced sequence of records. /// property value. - protected Provider(ProviderType type, params Provider[] sources) + protected Provider(ProviderType type, RecordSetHeader header, params Provider[] sources) { Type = type; + Header = header; Sources = sources; } } From dd5f3af054dcee1c8e9324c84864519f9b03cd34 Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 26 Jan 2022 22:06:31 -0800 Subject: [PATCH 42/64] Simplify Provider class code --- .../Orm/Rse/Providers/Provider.cs | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs index 243295d9db..51c04fd216 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2020 Xtensive LLC. +// Copyright (C) 2008-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov @@ -23,36 +23,23 @@ public abstract class Provider private const string ToString_Parameters = " ({0})"; private const int ToString_IndentSize = 2; - private Provider[] sources; - /// /// Gets of the current instance. /// - public ProviderType Type { get; private set; } + public ProviderType Type { get; } /// /// Gets or sets the source providers /// "consumed" by this provider to produce results of current provider. /// - public Provider[] Sources { - [DebuggerStepThrough] - get { return sources; } - [DebuggerStepThrough] - private set { - if (sources!=null) - throw Exceptions.AlreadyInitialized("Sources"); - sources = value; - } - } + public Provider[] Sources { get; } /// /// Gets or sets the header of the record sequence this provide produces. /// public RecordSetHeader Header { get; } - private string DebuggerDisplayName { - get { return GetType().GetShortName(); } - } + private string DebuggerDisplayName => GetType().GetShortName(); #region ToString method @@ -67,9 +54,10 @@ public sealed override string ToString() private void AppendBodyTo(StringBuilder sb, int indent) { AppendTitleTo(sb, indent); - indent = indent + ToString_IndentSize; - foreach (var source in Sources) + indent += ToString_IndentSize; + foreach (var source in Sources) { source.AppendBodyTo(sb, indent); + } } private void AppendTitleTo(StringBuilder sb, int indent) @@ -85,8 +73,9 @@ private string TitleToString() string parameters = ParametersToString(); sb.Append(providerName); - if (!parameters.IsNullOrEmpty()) + if (!parameters.IsNullOrEmpty()) { sb.AppendFormat(ToString_Parameters, parameters); + } return sb.ToString(); } From 0835f374dc1fa9b8adec716cfafee5b2641e7c5e Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 26 Jan 2022 22:14:15 -0800 Subject: [PATCH 43/64] Simplify code of the ExecutableProvider class --- .../Orm/Rse/Providers/ExecutableProvider.cs | 4 ++-- .../Orm/Rse/Providers/ExecutableProvider{TOrigin}.cs | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs index e9dcd87ef0..c52c6a43ed 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2020 Xtensive LLC. +// Copyright (C) 2008-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov @@ -23,7 +23,7 @@ public abstract class ExecutableProvider : Provider /// /// Gets the provider this provider is compiled from. /// - public CompilableProvider Origin { get; private set; } + public CompilableProvider Origin { get; } #region OnXxxEnumerate methods (to override) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider{TOrigin}.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider{TOrigin}.cs index 585b9648c7..9b04e3735b 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider{TOrigin}.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider{TOrigin}.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2008-2022 Xtensive LLC. +// This code is distributed under MIT license terms. +// See the License.txt file in the project root for more information. // Created by: Alex Yakunin // Created: 2008.08.13 @@ -20,10 +20,7 @@ public abstract class ExecutableProvider : ExecutableProvider /// /// Gets the provider this provider is compiled from. /// - public new TOrigin Origin { - get { return (TOrigin) base.Origin; } - } - + public new TOrigin Origin => (TOrigin) base.Origin; // Constructors From 7840f5cdfaad670da9b60dfe51198109e517c9bd Mon Sep 17 00:00:00 2001 From: Alex Ustinov Date: Wed, 26 Jan 2022 23:27:01 -0800 Subject: [PATCH 44/64] Remove remaining argument checks in providers hierarchy --- Directory.Build.props | 2 +- .../Core/PreconditionValidator.cs | 18 ++++++++++++++ Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs | 18 ++------------ .../Orm/Providers/SqlTemporaryDataProvider.cs | 3 +-- .../Providers/Compilable/BinaryProvider.cs | 3 --- .../Providers/Compilable/ExceptProvider.cs | 3 --- .../Providers/Compilable/FilterProvider.cs | 16 +------------ .../Providers/Compilable/IncludeProvider.cs | 5 ---- .../Providers/Compilable/IntersectProvider.cs | 4 ---- .../Rse/Providers/Compilable/RawProvider.cs | 10 ++------ .../Providers/Compilable/RowNumberProvider.cs | 2 +- .../Rse/Providers/Compilable/SeekProvider.cs | 2 +- .../Providers/Compilable/SelectProvider.cs | 12 +--------- .../Rse/Providers/Compilable/StoreProvider.cs | 24 +------------------ .../Rse/Providers/Compilable/TagProvider.cs | 2 -- .../Rse/Providers/Compilable/UnaryProvider.cs | 2 -- .../Rse/Providers/Compilable/UnionProvider.cs | 3 --- .../Rse/Providers/Compilable/VoidProvider.cs | 3 --- .../Executable/ExecutableRawProvider.cs | 6 +---- .../Orm/Rse/Providers/ExecutableProvider.cs | 2 +- .../Orm/Rse/Providers/Provider.cs | 2 +- 21 files changed, 32 insertions(+), 110 deletions(-) create mode 100644 Orm/Xtensive.Orm/Core/PreconditionValidator.cs diff --git a/Directory.Build.props b/Directory.Build.props index 79b83e1174..bcc4ba7d2c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -41,7 +41,7 @@ true true net6.0 - 9.0 + 10.0 $([MSBuild]::EnsureTrailingSlash( $([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildThisFileDirectory)', 'Orm.sln')))) Debug diff --git a/Orm/Xtensive.Orm/Core/PreconditionValidator.cs b/Orm/Xtensive.Orm/Core/PreconditionValidator.cs new file mode 100644 index 0000000000..e8f6bc3bbb --- /dev/null +++ b/Orm/Xtensive.Orm/Core/PreconditionValidator.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +internal static class PreconditionValidator +{ + [return: NotNull] + public static T NotNull( + [NotNull] this T obj, + string message = default, + [CallerArgumentExpression("obj")] + string parameterName = default) + where T : class + { + return obj ?? throw new ArgumentNullException(parameterName, message); + } + +} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs index 53acdb3b61..b9c450fb1a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlProvider.cs @@ -4,7 +4,6 @@ // Created by: Alexey Kochetov // Created: 2008.07.11 -using System.Linq; using Xtensive.Core; using Xtensive.Sql; using Xtensive.Sql.Dml; @@ -21,30 +20,17 @@ public class SqlProvider : ExecutableProvider { protected readonly HandlerAccessor handlers; - private const string ParameterNamePrefix = "@p"; - private const string ToStringFormat = "[Command: \"{0}\"]"; private SqlTable permanentReference; /// /// Gets associated with this provider. /// - public QueryRequest Request { get; private set; } + public QueryRequest Request { get; } /// /// Gets the permanent reference () for associated with this provider. /// - public SqlTable PermanentReference { - get { - if (ReferenceEquals(null, permanentReference)) - permanentReference = SqlDml.QueryRef(Request.Statement); - return permanentReference; - } - } - - /// - /// Gets the domain handler this provider is bound to. - /// - protected Providers.DomainHandler DomainHandler { get { return handlers.DomainHandler; } } + public SqlTable PermanentReference => permanentReference ??= SqlDml.QueryRef(Request.Statement); /// protected internal override DataReader OnEnumerate(Rse.Providers.EnumerationContext context) diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs index 8814c7f506..aa4dd0fde9 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs @@ -7,7 +7,6 @@ using System; using Xtensive.Core; using System.Collections.Generic; -using Xtensive.Tuples; using Tuple = Xtensive.Tuples.Tuple; using Xtensive.Orm.Rse.Providers; @@ -25,7 +24,7 @@ public abstract class SqlTemporaryDataProvider : SqlProvider protected void LockAndStore(Rse.Providers.EnumerationContext context, IEnumerable data) { var storageContext = (EnumerationContext) context; - var tableLock = DomainHandler.TemporaryTableManager.Acquire(storageContext, tableDescriptor); + var tableLock = handlers.DomainHandler.TemporaryTableManager.Acquire(storageContext, tableDescriptor); if (tableLock == null) return; storageContext.SetValue(this, TemporaryTableLockName, tableLock); diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs index dc9d19d080..8d0211ccb8 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/BinaryProvider.cs @@ -51,9 +51,6 @@ protected BinaryProvider(ProviderType type, CompilableProvider left, CompilableP protected BinaryProvider(ProviderType type, RecordSetHeader header, CompilableProvider left, CompilableProvider right) : base(type, header, left, right) { - ArgumentValidator.EnsureArgumentNotNull(header, nameof(header)); - ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); - ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); Left = left; Right = right; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs index 3c7ec80f4f..e412410619 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs @@ -22,9 +22,6 @@ public sealed class ExceptProvider : BinaryProvider private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); - ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); - var leftHeader = left.Header; var leftDescriptor = leftHeader.TupleDescriptor; var rightDescriptor = right.Header.TupleDescriptor; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs index cb5fde3cfe..54fc42e577 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FilterProvider.cs @@ -20,23 +20,10 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class FilterProvider : UnaryProvider { - private Func compiledPredicate; - /// /// Filtering predicate expression. /// - public Expression> Predicate { get; private set; } - - /// - /// Gets the compiled . - /// - public Func CompiledPredicate { - get { - if (compiledPredicate==null) - compiledPredicate = Predicate.CachingCompile(); - return compiledPredicate; - } - } + public Expression> Predicate { get; } /// protected override string ParametersToString() @@ -44,7 +31,6 @@ protected override string ParametersToString() return Predicate.ToString(true); } - // Constructors /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index a213727c29..a5bd779d30 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -58,10 +58,6 @@ public sealed class IncludeProvider: UnaryProvider, private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( CompilableProvider source, IReadOnlyList filteredColumns, string resultColumnName, out TupleDescriptor filteredTupleDescriptor) { - ArgumentValidator.EnsureArgumentNotNull(source, nameof(source)); - ArgumentValidator.EnsureArgumentNotNull(filteredColumns, nameof(filteredColumns)); - ArgumentValidator.EnsureArgumentNotNullOrEmpty(resultColumnName, nameof(resultColumnName)); - var header = source.Header.Add(new SystemColumn(resultColumnName, 0, WellKnownTypes.Bool)); var columnCount = filteredColumns.Count; var fieldTypes = new Type[columnCount]; @@ -85,7 +81,6 @@ public IncludeProvider(CompilableProvider source, IncludeAlgorithm algorithm, bo Expression>> filterDataSource, string resultColumnName, IReadOnlyList filteredColumns) : base(ProviderType.Include, BuildHeaderAndFilteredTupleDescriptor(source, filteredColumns, resultColumnName, out var filteredTupleDescriptor), source) { - ArgumentValidator.EnsureArgumentNotNull(filterDataSource, nameof(FilterDataSource)); Algorithm = algorithm; IsInlined = isInlined; FilterDataSource = filterDataSource; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs index 9f4ae1a47e..61b91bf0aa 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs @@ -17,14 +17,10 @@ namespace Xtensive.Orm.Rse.Providers public sealed class IntersectProvider : BinaryProvider { - // Constructors private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); - ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); - var leftHeader = left.Header; var leftDescriptor = leftHeader.TupleDescriptor; var rightDescriptor = right.Header.TupleDescriptor; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs index 26fbdf182a..b38dadcd8e 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RawProvider.cs @@ -25,18 +25,12 @@ public sealed class RawProvider : CompilableProvider /// /// Raw data source - an array of tuples. /// - public Expression>> Source { get; private set; } + public Expression>> Source { get; } /// /// Gets the compiled . /// - public Func> CompiledSource { - get { - if (compiledSource==null) - compiledSource = Source.CachingCompile(); - return compiledSource; - } - } + public Func> CompiledSource => compiledSource ??= Source.CachingCompile(); /// protected override string ParametersToString() diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 5936b62253..a23021d52b 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -18,7 +18,7 @@ public sealed class RowNumberProvider : UnaryProvider /// /// Gets the row number column. /// - public SystemColumn SystemColumn { get; private set; } + public SystemColumn SystemColumn { get; } // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs index dbd2814e51..c93ba61dd6 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SeekProvider.cs @@ -19,7 +19,7 @@ public sealed class SeekProvider : UnaryProvider /// /// Seek parameter. /// - public Func Key { get; private set; } + public Func Key { get; } /// protected override string ParametersToString() diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs index 2fe33bde2a..239b465b17 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs @@ -38,17 +38,7 @@ protected override string ParametersToString() public SelectProvider(CompilableProvider provider, IReadOnlyList columnIndexes) : base(ProviderType.Select, provider.Header.Select(columnIndexes), provider) { - switch (columnIndexes) { - case int[] indexArray: - ColumnIndexes = Array.AsReadOnly(indexArray); - break; - case List indexList: - ColumnIndexes = indexList.AsReadOnly(); - break; - default: - ColumnIndexes = columnIndexes; - break; - } + ColumnIndexes = columnIndexes; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs index 69a545f345..3ff8d05897 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs @@ -5,7 +5,6 @@ // Created: 2008.09.05 using System; -using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers { @@ -35,20 +34,6 @@ protected override string ParametersToString() // Constructors - /// - /// Initializes a new instance of this class. - /// - /// The property value. - /// The property value. - public StoreProvider(RecordSetHeader header, string name) - : base (ProviderType.Store, header) - { - ArgumentValidator.EnsureArgumentNotNull(header, "header"); - ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, "name"); - - Name = name; - } - /// /// Initializes a new instance of this class. /// @@ -57,9 +42,6 @@ public StoreProvider(RecordSetHeader header, string name) public StoreProvider(Provider source, string name) : base(ProviderType.Store, source.Header, source) { - ArgumentValidator.EnsureArgumentNotNull(source, "source"); - ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, "name"); - Name = name; Source = source; } @@ -69,12 +51,8 @@ public StoreProvider(Provider source, string name) /// /// The property value. public StoreProvider(Provider source) - : base(ProviderType.Store, source.Header, source) + : this(source, Guid.NewGuid().ToString()) { - ArgumentValidator.EnsureArgumentNotNull(source, "source"); - - Name = Guid.NewGuid().ToString(); - Source = source; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs index 5e443e5b75..ad5cbb1e99 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs @@ -5,7 +5,6 @@ // Created: 2021.09.13 using System; -using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers @@ -23,7 +22,6 @@ public sealed class TagProvider : UnaryProvider public TagProvider(CompilableProvider source, string tag) : base(ProviderType.Tag, source) { - ArgumentValidator.EnsureArgumentNotNullOrEmptyOrWhiteSpace(tag, nameof(tag)); Tag = tag; } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs index 334cbbc356..af4d8bae3a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs @@ -41,9 +41,7 @@ protected UnaryProvider(ProviderType type, CompilableProvider source) protected UnaryProvider(ProviderType type, RecordSetHeader header, CompilableProvider source) : base(type, header, source) { - ArgumentValidator.EnsureArgumentNotNull(source, "source"); Source = source; } - } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs index e3f4373a3d..b908f2c988 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs @@ -23,9 +23,6 @@ public sealed class UnionProvider : BinaryProvider private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - ArgumentValidator.EnsureArgumentNotNull(left, nameof(left)); - ArgumentValidator.EnsureArgumentNotNull(right, nameof(right)); - var leftHeader = left.Header; var rightHeader = right.Header; EnsureUnionIsPossible(leftHeader, rightHeader); diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs index 538429f0ac..1d234ed268 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs @@ -4,8 +4,6 @@ // Created by: Denis Krjuchkov // Created: 2012.01.29 -using Xtensive.Core; - namespace Xtensive.Orm.Rse.Providers { public sealed class VoidProvider : CompilableProvider @@ -14,7 +12,6 @@ public sealed class VoidProvider : CompilableProvider public VoidProvider(RecordSetHeader header) : base(ProviderType.Void, header) { - ArgumentValidator.EnsureArgumentNotNull(header, "header"); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs index 035d2cb9b8..4bd6cd9f54 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2020 Xtensive LLC. +// Copyright (C) 2008-2022 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kochetov @@ -17,12 +17,8 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ExecutableRawProvider : ExecutableProvider { - #region Cached properties - private const string CachedSourceName = "CachedSource"; - #endregion - /// protected internal override void OnBeforeEnumerate(EnumerationContext context) { diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs index c52c6a43ed..415966dea5 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs @@ -156,7 +156,7 @@ public async Task GetRecordSetReaderAsync( /// /// The property value. /// The property value. - protected ExecutableProvider(CompilableProvider origin, params ExecutableProvider[] sources) + protected ExecutableProvider(CompilableProvider origin, ExecutableProvider[] sources) : base(origin.Type, origin.Header, sources) { Origin = origin; diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs index 51c04fd216..03f61c7a88 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs @@ -100,7 +100,7 @@ protected virtual string ParametersToString() /// The type of the provider. /// The header of the produced sequence of records. /// property value. - protected Provider(ProviderType type, RecordSetHeader header, params Provider[] sources) + protected Provider(ProviderType type, RecordSetHeader header, Provider[] sources) { Type = type; Header = header; From 18a8a601169f4f03ce18072fed7a38ad316e7930 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 26 Mar 2026 13:14:36 +0500 Subject: [PATCH 45/64] Organizational changes of building provider headers being in region, not in constructor section --- .../Rse/Providers/Compilable/AggregateProvider.cs | 5 +++-- .../Rse/Providers/Compilable/CalculateProvider.cs | 13 ++++++------- .../Orm/Rse/Providers/Compilable/ConcatProvider.cs | 3 ++- .../Providers/Compilable/ContainsTableProvider.cs | 10 ++++++---- .../Orm/Rse/Providers/Compilable/ExceptProvider.cs | 2 ++ .../Rse/Providers/Compilable/ExistenceProvider.cs | 3 ++- .../Rse/Providers/Compilable/FreeTextProvider.cs | 2 ++ .../Orm/Rse/Providers/Compilable/IncludeProvider.cs | 3 ++- .../Rse/Providers/Compilable/IntersectProvider.cs | 2 ++ .../Rse/Providers/Compilable/RowNumberProvider.cs | 2 ++ .../Orm/Rse/Providers/Compilable/UnionProvider.cs | 3 ++- 11 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index 57bc089315..ee2f8f4ca4 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -157,6 +157,7 @@ private static NotSupportedException AggregateNotSupported(Type sourceColumnType #endregion + #region Header build private static RecordSetHeader BuildHeaderAndColumns( CompilableProvider source, IReadOnlyList columnDescriptors, @@ -196,6 +197,7 @@ private static RecordSetHeader BuildHeaderAndColumns( return sourceHeader.Select(groupIndexes).Add(aggregateColumns); } + #endregion // Constructors @@ -207,7 +209,7 @@ private static RecordSetHeader BuildHeaderAndColumns( /// The descriptors of . [Obsolete] public AggregateProvider(CompilableProvider source, int[] groupIndexes, params AggregateColumnDescriptor[] columnDescriptors) - : base(ProviderType.Aggregate, BuildHeaderAndColumns(source, columnDescriptors, ref groupIndexes, out var columns), source) + : this(source,groupIndexes, (IReadOnlyList)columnDescriptors) { } @@ -220,7 +222,6 @@ public AggregateProvider(CompilableProvider source, int[] groupIndexes, params A public AggregateProvider(CompilableProvider source, int[] groupIndexes, IReadOnlyList columnDescriptors) : base(ProviderType.Aggregate, BuildHeaderAndColumns(source, columnDescriptors, ref groupIndexes, out var columns), source) { - ArgumentValidator.EnsureArgumentNotNull(columnDescriptors, nameof(columnDescriptors)); AggregateColumns = columns; GroupColumnIndexes = groupIndexes; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index e59efdec6a..2afdcee41f 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -33,9 +33,7 @@ protected override string ParametersToString() return CalculatedColumns.ToCommaDelimitedString(); } - - // Constructors - + #region Header build private static RecordSetHeader BuildHeaderAndColumns( CompilableProvider source, IReadOnlyList columnDescriptors, @@ -51,6 +49,9 @@ private static RecordSetHeader BuildHeaderAndColumns( return sourceHeader.Add(calculatedColumns); } + #endregion + + // Constructors /// /// Initializes a new instance of this class. @@ -59,7 +60,7 @@ private static RecordSetHeader BuildHeaderAndColumns( /// The descriptors of . [Obsolete] public CalculateProvider(CompilableProvider source, params CalculatedColumnDescriptor[] columnDescriptors) - : this(source, false, columnDescriptors) + : this(source, columnDescriptors, false) { } @@ -71,10 +72,8 @@ public CalculateProvider(CompilableProvider source, params CalculatedColumnDescr /// The descriptors of . [Obsolete] public CalculateProvider(CompilableProvider source, bool isInlined, params CalculatedColumnDescriptor[] columnDescriptors) - : base(ProviderType.Calculate, BuildHeaderAndColumns(source, columnDescriptors, out var calculatedColumns), source) + : this(source, columnDescriptors, isInlined) { - IsInlined = isInlined; - CalculatedColumns = calculatedColumns; } /// diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs index 1b6c206fe5..cfa3861502 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs @@ -17,7 +17,7 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ConcatProvider : BinaryProvider { - + #region Header build private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { var leftHeader = left.Header; @@ -59,6 +59,7 @@ private static void EnsureConcatIsPossible(RecordSetHeader leftHeader, RecordSet throw new InvalidOperationException(string.Format(Strings.ExXCantBeExecuted, "Concatenation")); } } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs index 66716a071b..3e1d366092 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs @@ -26,11 +26,10 @@ public sealed class ContainsTableProvider : CompilableProvider public Func TopN { get; } - public IReadOnlyList TargetColumns { get; } - - - // Constructors + public IReadOnlyList TargetColumns { get; } + + #region Header build private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { if (fullFeatured) { @@ -55,6 +54,9 @@ private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankC return new RecordSetHeader(tupleDescriptor, columns); } } + #endregion + + // Constructors public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, bool fullFeatured) : this(index, searchCriteria, rankColumnName, targetColumns, null, fullFeatured) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs index 3e09673b26..e472702345 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExceptProvider.cs @@ -16,6 +16,7 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ExceptProvider : BinaryProvider { + #region Header build private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { var leftHeader = left.Header; @@ -26,6 +27,7 @@ private static RecordSetHeader BuildHeader(CompilableProvider left, CompilablePr } return leftHeader; } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs index de3a6ba352..c736e9070a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs @@ -24,12 +24,13 @@ public sealed class ExistenceProvider : UnaryProvider /// public string ExistenceColumnName { get; } - + #region Header build private static RecordSetHeader BuildHeader(string existenceColumnName) { return new RecordSetHeader( BoolTupleDescriptor, new[] { new SystemColumn(existenceColumnName, 0, WellKnownTypes.Bool) }); } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs index dfd35ccb35..5ae41c5009 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs @@ -28,6 +28,7 @@ public sealed class FreeTextProvider : CompilableProvider public bool FullFeatured { get; } + #region Header build private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { if (fullFeatured) { @@ -51,6 +52,7 @@ private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankC return new RecordSetHeader(tupleDescriptor, columns); } } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 49a1e4f21f..288f4cd6c3 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -52,7 +52,7 @@ public sealed class IncludeProvider: UnaryProvider, public TupleDescriptor FilteredTupleDescriptor { get; } - + #region Header build private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( CompilableProvider source, IReadOnlyList filteredColumns, string resultColumnName, out TupleDescriptor filteredTupleDescriptor) { @@ -65,6 +65,7 @@ private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( filteredTupleDescriptor = TupleDescriptor.Create(fieldTypes); return header; } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs index 68245b633e..de2a4f7a3b 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IntersectProvider.cs @@ -16,6 +16,7 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class IntersectProvider : BinaryProvider { + #region Header build private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { var leftHeader = left.Header; @@ -26,6 +27,7 @@ private static RecordSetHeader BuildHeader(CompilableProvider left, CompilablePr } return leftHeader; } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs index 0d327009b3..03e4604754 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/RowNumberProvider.cs @@ -20,12 +20,14 @@ public sealed class RowNumberProvider : UnaryProvider /// public SystemColumn SystemColumn { get; } + #region Header build private static RecordSetHeader CreateHeaderAndColumn(CompilableProvider source, string columnName, out SystemColumn systemColumn) { var sourceHeader = source.Header; systemColumn = new SystemColumn(columnName, sourceHeader.Length, WellKnownTypes.Int64); return sourceHeader.Add(systemColumn); } + #endregion // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs index 77ec42bb76..41988fd41e 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs @@ -18,7 +18,7 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class UnionProvider : BinaryProvider { - + #region Header build private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { var leftHeader = left.Header; @@ -60,6 +60,7 @@ private static void EnsureUnionIsPossible(RecordSetHeader leftHeader, RecordSetH throw new InvalidOperationException(string.Format(Strings.ExXCantBeExecuted, "Union operation")); } } + #endregion // Constructors From c23c81e056471d5238993f87d8779e084a56d351 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 26 Mar 2026 13:15:52 +0500 Subject: [PATCH 46/64] Rse.Providers.Provider validates header and sources for not having nulls + some warnings resolved --- .../Orm/Rse/Providers/Provider.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs index 83bb6f29e2..49b7852ce8 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs @@ -63,19 +63,19 @@ private void AppendBodyTo(StringBuilder sb, int indent) private void AppendTitleTo(StringBuilder sb, int indent) { - sb.Append(TitleToString().Indent(indent)) + _ = sb.Append(TitleToString().Indent(indent)) .AppendLine(); } private string TitleToString() { var sb = new StringBuilder(); - string providerName = GetType().GetShortName().TryCutSuffix(ToString_ProviderTypeSuffix); - string parameters = ParametersToString(); + var providerName = GetType().GetShortName().TryCutSuffix(ToString_ProviderTypeSuffix); + var parametersAsString = ParametersToString(); - sb.Append(providerName); - if (!parameters.IsNullOrEmpty()) { - sb.AppendFormat(ToString_Parameters, parameters); + _ = sb.Append(providerName); + if (!parametersAsString.IsNullOrEmpty()) { + _ = sb.AppendFormat(ToString_Parameters, parametersAsString); } return sb.ToString(); } @@ -85,10 +85,7 @@ private string TitleToString() /// for the method. /// /// Provider parameters as a single line string. - protected virtual string ParametersToString() - { - return string.Empty; - } + protected virtual string ParametersToString() => string.Empty; #endregion @@ -104,8 +101,10 @@ protected virtual string ParametersToString() protected Provider(ProviderType type, RecordSetHeader header, Provider[] sources) { Type = type; - Header = header; - Sources = sources; + Header = header ?? throw new ArgumentNullException(nameof(header)); + Sources = (sources.Any(p => p is null)) + ? throw new ArgumentNullException(nameof(header)) + : sources; } } } From 63ebab5cb9804c767e90dc34518d9317803218ee Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 26 Mar 2026 15:08:30 +0500 Subject: [PATCH 47/64] Caching IPreCompiler wrappers for rewriters --- .../Orm/Providers/DomainHandler.cs | 38 +++---------------- .../Transformation/ApplyProviderCorrector.cs | 18 ++++++++- .../Rse/Transformation/OrderingCorrector.cs | 32 ++++++++++++++++ .../Rse/Transformation/SkipTakeCorrector.cs | 24 +++++++++++- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs index 42c4d10977..d3161b433e 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs @@ -20,8 +20,6 @@ namespace Xtensive.Orm.Providers /// public abstract class DomainHandler : DomainBoundHandler { - private static readonly OrderingCorrector OrderingCorrector = new OrderingCorrector(ResolveOrderingDescriptor); - private Dictionary memberCompilerProviders; /// @@ -91,16 +89,13 @@ protected virtual IPreCompiler CreatePreCompiler(CompilerConfiguration configura { var providerInfo = Handlers.ProviderInfo; - var applyCorrector = new ApplyProviderCorrector( - !providerInfo.Supports(ProviderFeatures.Apply)); - var skipTakeCorrector = new SkipTakeCorrector( - providerInfo.Supports(ProviderFeatures.NativeTake), - providerInfo.Supports(ProviderFeatures.NativeSkip)); return new CompositePreCompiler( - applyCorrector, - skipTakeCorrector, + ApplyProviderCorrector.GetOrCreate(!providerInfo.Supports(ProviderFeatures.Apply)), + SkipTakeCorrector.GetOrCreate( + providerInfo.Supports(ProviderFeatures.NativeTake), + providerInfo.Supports(ProviderFeatures.NativeSkip)), RedundantColumnOptimizer.Instance, - OrderingCorrector); + OrderingCorrector.DefaultResolverInstance); } /// @@ -171,29 +166,6 @@ private void BuildQueryPreprocessors() QueryPreprocessors = ordered ?? throw new InvalidOperationException(Strings.ExCyclicDependencyInQueryPreprocessorGraphIsDetected); } - private static ProviderOrderingDescriptor ResolveOrderingDescriptor(CompilableProvider provider) - { - var isOrderSensitive = provider.Type is ProviderType.Skip - or ProviderType.Take - or ProviderType.Seek - or ProviderType.Paging - or ProviderType.RowNumber; - var preservesOrder = provider.Type is ProviderType.Skip - or ProviderType.Take - or ProviderType.Seek - or ProviderType.Paging - or ProviderType.RowNumber - or ProviderType.Distinct - or ProviderType.Alias; - var isOrderBreaker = provider.Type is ProviderType.Except - or ProviderType.Intersect - or ProviderType.Union - or ProviderType.Concat - or ProviderType.Existence; - var isSorter = provider.Type is ProviderType.Sort or ProviderType.Index; - return new ProviderOrderingDescriptor(isOrderSensitive, preservesOrder, isOrderBreaker, isSorter); - } - #endregion diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs index d7061d2222..7f2f1eb854 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs @@ -15,8 +15,24 @@ namespace Xtensive.Orm.Rse.Transformation /// public sealed class ApplyProviderCorrector : IPreCompiler { + private static readonly Lazy exceptionThrowingCorrector = new(() => new ApplyProviderCorrector(true)); + private static readonly Lazy silentCorrector = new(() => new ApplyProviderCorrector(false)); + private readonly bool throwOnCorrectionFault; + /// + /// Gets existing instance or creates new one and cache it. + /// + /// if set to + /// then will be thrown in case of + /// the correction's fault; otherwise the origin + /// will be returned. + public static ApplyProviderCorrector GetOrCreate(bool throwOnCorrectionFault) => + (throwOnCorrectionFault) + ? exceptionThrowingCorrector.Value + : silentCorrector.Value; + + /// public CompilableProvider Process(CompilableProvider rootProvider) { @@ -33,7 +49,7 @@ public CompilableProvider Process(CompilableProvider rootProvider) /// then will be thrown in case of /// the correction's fault; otherwise the origin /// will be returned. - public ApplyProviderCorrector(bool throwOnCorrectionFault) + private ApplyProviderCorrector(bool throwOnCorrectionFault) { this.throwOnCorrectionFault = throwOnCorrectionFault; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs index 0314b07a6c..0971c3fa7d 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs @@ -18,6 +18,8 @@ namespace Xtensive.Orm.Rse.Transformation [Serializable] public sealed class OrderingCorrector : IPreCompiler { + public static OrderingCorrector DefaultResolverInstance { get; } = new OrderingCorrector(); + private readonly Func orderingDescriptorResolver; /// @@ -26,6 +28,28 @@ CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) return OrderingRewriter.Rewrite(rootProvider, orderingDescriptorResolver); } + private static ProviderOrderingDescriptor ResolveOrderingDescriptor(CompilableProvider provider) + { + var isOrderSensitive = provider.Type is ProviderType.Skip + or ProviderType.Take + or ProviderType.Seek + or ProviderType.Paging + or ProviderType.RowNumber; + var preservesOrder = provider.Type is ProviderType.Skip + or ProviderType.Take + or ProviderType.Seek + or ProviderType.Paging + or ProviderType.RowNumber + or ProviderType.Distinct + or ProviderType.Alias; + var isOrderBreaker = provider.Type is ProviderType.Except + or ProviderType.Intersect + or ProviderType.Union + or ProviderType.Concat + or ProviderType.Existence; + var isSorter = provider.Type is ProviderType.Sort or ProviderType.Index; + return new ProviderOrderingDescriptor(isOrderSensitive, preservesOrder, isOrderBreaker, isSorter); + } // Constructors @@ -39,5 +63,13 @@ public OrderingCorrector(Func or ArgumentValidator.EnsureArgumentNotNull(orderingDescriptorResolver, "orderingDescriptorResolver"); this.orderingDescriptorResolver = orderingDescriptorResolver; } + + /// + /// Initializes a new instance of this class with default resolver + /// + public OrderingCorrector() + { + this.orderingDescriptorResolver = ResolveOrderingDescriptor; + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs index 47ab3e89a3..b2d8890dc6 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs @@ -16,9 +16,31 @@ namespace Xtensive.Orm.Rse.Transformation [Serializable] public sealed class SkipTakeCorrector : IPreCompiler { + private static readonly Lazy fullPagingSupport = new(() => new SkipTakeCorrector(true, true)); + private static readonly Lazy takeOnlySupport = new(() => new SkipTakeCorrector(true, false)); + private static readonly Lazy skipOnlySupport = new(() => new SkipTakeCorrector(false, true)); + private static readonly Lazy noPagingSupport = new(() => new SkipTakeCorrector(false, false)); + private readonly bool takeSupported; private readonly bool skipSupported; + + /// + /// Gets cached instance of create and cache new one + /// + /// Take operation is supported. + /// Skip operation is supported. + /// Cached instance of corrector. + public static SkipTakeCorrector GetOrCreate(bool takeSupported, bool skipSupported) + { + return (takeSupported, skipSupported) switch { + (true, true) => fullPagingSupport.Value, + (false, true) => skipOnlySupport.Value, + (true, false) => takeOnlySupport.Value, + (false, false) => noPagingSupport.Value + }; + } + /// CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) { @@ -28,7 +50,7 @@ CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) // Constructors - public SkipTakeCorrector(bool takeSupported, bool skipSupported) + private SkipTakeCorrector(bool takeSupported, bool skipSupported) { this.takeSupported = takeSupported; this.skipSupported = skipSupported; From 022c688305e5c899b7a967ed57d8cdf8dcd298aa Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Mar 2026 17:35:51 +0500 Subject: [PATCH 48/64] Fix merge error of wrong index of calculated column in IncludeProvider's header --- .../Orm/Rse/Providers/Compilable/IncludeProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 288f4cd6c3..da52d672aa 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -56,7 +56,7 @@ public sealed class IncludeProvider: UnaryProvider, private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( CompilableProvider source, IReadOnlyList filteredColumns, string resultColumnName, out TupleDescriptor filteredTupleDescriptor) { - var header = source.Header.Add(new SystemColumn(resultColumnName, 0, WellKnownTypes.Bool)); + var header = source.Header.Add(new SystemColumn(resultColumnName, source.Header.Length, WellKnownTypes.Bool)); var columnCount = filteredColumns.Count; var fieldTypes = new Type[columnCount]; for (var index = 0; index < columnCount; index++) { From 99ff9c5a0b0b69d7eafd3845fa149c68e094808d Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 26 Mar 2026 17:30:10 +0500 Subject: [PATCH 49/64] Improved Header building for certain providers --- .../Providers/Compilable/ConcatProvider.cs | 17 +++++++---- .../Compilable/ContainsTableProvider.cs | 13 ++++----- .../Providers/Compilable/FreeTextProvider.cs | 12 +++----- .../Rse/Providers/Compilable/UnionProvider.cs | 29 ++++++++++++------- 4 files changed, 39 insertions(+), 32 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs index cfa3861502..3bb954dfe0 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs @@ -5,6 +5,7 @@ // Created: 2009.04.01 using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; @@ -23,24 +24,30 @@ private static RecordSetHeader BuildHeader(CompilableProvider left, CompilablePr var leftHeader = left.Header; var rightHeader = right.Header; EnsureConcatIsPossible(leftHeader, rightHeader); + var mappedColumnIndexes = new List(); - var columns = new List(); - for (int i = 0; i < leftHeader.Columns.Count; i++) { + var rented = ArrayPool.Shared.Rent(Math.Max(leftHeader.Columns.Count, 64)); + var lastIndex = 0; + for (int i = 0, count = leftHeader.Columns.Count; i < count; i++) { var leftColumn = leftHeader.Columns[i]; var rightColumn = rightHeader.Columns[i]; if (leftColumn is MappedColumn leftMappedColumn && rightColumn is MappedColumn rightMappedColumn) { if (leftMappedColumn.ColumnInfoRef.Equals(rightMappedColumn.ColumnInfoRef)) { - columns.Add(leftMappedColumn); + rented[lastIndex++] = leftColumn; mappedColumnIndexes.Add(i); } else { - columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); + rented[lastIndex++] = new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type); } } else { - columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); + rented[lastIndex++] = new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type); } } + var columns = new Column[lastIndex]; + Array.Copy(rented, columns, lastIndex); + ArrayPool.Shared.Return(rented, true); + var columnGroups = leftHeader.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); return new RecordSetHeader( diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs index 3e1d366092..7f84cb8a25 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ContainsTableProvider.cs @@ -42,15 +42,12 @@ private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankC var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; if (primaryIndexKeyColumns.Count!=1) throw new InvalidOperationException(Strings.ExOnlySingleColumnKeySupported); - var fieldTypes = primaryIndexKeyColumns - .Select(static columnInfo => columnInfo.Key.ValueType) - .Append(WellKnownTypes.Double) - .ToArray(primaryIndexKeyColumns.Count + 1); + + var keyValueType = primaryIndexKeyColumns[0].Key.ValueType; + var fieldTypes = new Type[2] { keyValueType, WellKnownTypes.Double }; var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - var columns = primaryIndexKeyColumns - .Select(static (c, i) => (Column) new MappedColumn("KEY", i, c.Key.ValueType)) - .Append(new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double)) - .ToArray(primaryIndexKeyColumns.Count + 1);; + var columns = new Column[2] { new MappedColumn("KEY", 0, keyValueType), new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double) }; + return new RecordSetHeader(tupleDescriptor, columns); } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs index 5ae41c5009..54f5cc7dce 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/FreeTextProvider.cs @@ -40,15 +40,11 @@ private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankC var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; if (primaryIndexKeyColumns.Count!=1) throw new InvalidOperationException(Strings.ExOnlySingleColumnKeySupported); - var fieldTypes = primaryIndexKeyColumns - .Select(static columnInfo => columnInfo.Key.ValueType) - .Append(WellKnownTypes.Double) - .ToArray(primaryIndexKeyColumns.Count + 1); + + var keyValueType = primaryIndexKeyColumns[0].Key.ValueType; + var fieldTypes = new Type[2] { keyValueType, WellKnownTypes.Double }; var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - var columns = primaryIndexKeyColumns - .Select(static (c, i) => (Column) new MappedColumn("KEY", i, c.Key.ValueType)) - .Append(new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double)) - .ToArray(primaryIndexKeyColumns.Count + 1); + var columns = new Column[2] { new MappedColumn("KEY", 0, keyValueType), new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double) }; return new RecordSetHeader(tupleDescriptor, columns); } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs index 41988fd41e..d95a3eb372 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs @@ -5,6 +5,7 @@ // Created: 2009.04.01 using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using Xtensive.Core; @@ -25,28 +26,34 @@ private static RecordSetHeader BuildHeader(CompilableProvider left, CompilablePr var rightHeader = right.Header; EnsureUnionIsPossible(leftHeader, rightHeader); var mappedColumnIndexes = new List(); - var columns = new List(); + // we can use shared array here and then fast-copy to be more memory-efficient and GC-friendly + var rented = ArrayPool.Shared.Rent(Math.Max(leftHeader.Columns.Count, 64)); // reduce pool growth + var lastIndex = 0; for (int i = 0; i < leftHeader.Columns.Count; i++) { var leftColumn = leftHeader.Columns[i]; var rightColumn = rightHeader.Columns[i]; - if (leftColumn is MappedColumn && rightColumn is MappedColumn) { - var leftMappedColumn = (MappedColumn) leftColumn; - var rightMappedColumn = (MappedColumn) rightColumn; + if (leftColumn is MappedColumn leftMappedColumn && rightColumn is MappedColumn rightMappedColumn) { if (leftMappedColumn.ColumnInfoRef.Equals(rightMappedColumn.ColumnInfoRef)) { - columns.Add(leftMappedColumn); + rented[lastIndex++] = leftColumn; mappedColumnIndexes.Add(i); - } - else - columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); + } + else { + rented[lastIndex++] = new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type); + } + } + else { + rented[lastIndex++] = new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type); } - else - columns.Add(new SystemColumn(leftColumn.Name, leftColumn.Index, leftColumn.Type)); } + var columns = new Column[lastIndex]; + Array.Copy(rented, columns, lastIndex); + ArrayPool.Shared.Return(rented, true); // not sure we can make it false, becasue + var columnGroups = leftHeader.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); return new RecordSetHeader( leftHeader.TupleDescriptor, - columns, + columns, columnGroups, null, null); From e7961aec13f2a4889ac66e97eb4cbd1b92bd0866 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 27 Mar 2026 19:52:48 +0500 Subject: [PATCH 50/64] No Enumerable.Last() calls --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs | 8 ++++---- Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs | 2 +- .../Orm/Internals/Prefetch/ReferencedEntityContainer.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs | 2 +- Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs | 2 +- Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Paging.cs | 2 +- Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs | 2 +- .../Orm/Rse/Transformation/ColumnMappingInspector.cs | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index b2305f572c..871bb6cfcd 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -949,7 +949,7 @@ private EntitySetTypeState GetEntitySetTypeState() private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, EntitySetBase entitySet) { - var association = field.Associations.Last(); + var association = field.Associations[^1]; var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); var seek = entitySet.Session.Compile(query); var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; @@ -1060,7 +1060,7 @@ protected EntitySetBase(Entity owner, FieldInfo field) this.owner = owner; Field = field; State = new EntitySetState(this); - var association = Field.Associations.Last(); + var association = Field.Associations[^1]; if (association.AuxiliaryType != null && association.IsMaster) { var domain = Session.Domain; var itemType = domain.Model.Types[Field.ItemType]; diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs index 2bf5073d79..a85b1cb0b8 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs @@ -56,7 +56,7 @@ internal sealed class EntitySetTask : IEquatable private static readonly Parameter itemCountLimitParameter = new Parameter("ItemCountLimit"); private static readonly Func CreateRecordSetLoadingItems = cachingKey => { - var association = cachingKey.ReferencingField.Associations.Last(); + var association = cachingKey.ReferencingField.Associations[^1]; var primaryTargetIndex = association.TargetType.Indexes.PrimaryIndex; var resultColumns = new List(primaryTargetIndex.Columns.Count); var result = association.AuxiliaryType == null @@ -104,7 +104,7 @@ public void UpdateCache() var reader = manager.Owner.Session.Domain.EntityDataReader; var records = reader.Read(itemsQueryTask.Result, QueryProvider.Header, manager.Owner.Session); var entityKeys = new List(itemsQueryTask.Result.Count); - var association = ReferencingField.Associations.Last(); + var association = ReferencingField.Associations[^1]; var auxEntities = (association.AuxiliaryType != null) ? new List>(itemsQueryTask.Result.Count) : null; @@ -191,7 +191,7 @@ private QueryTask CreateQueryTask() private static CompilableProvider CreateQueryForAssociationViaAuxType(in ItemsQueryCacheKey cachingKey, IndexInfo primaryTargetIndex, List resultColumns) { - var association = cachingKey.ReferencingField.Associations.Last(); + var association = cachingKey.ReferencingField.Associations[^1]; var associationIndex = association.UnderlyingIndex; var joiningColumns = GetJoiningColumnIndexes(primaryTargetIndex, associationIndex, association.AuxiliaryType != null); @@ -213,7 +213,7 @@ private static CompilableProvider CreateQueryForAssociationViaAuxType(in ItemsQu private static CompilableProvider CreateQueryForDirectAssociation(in ItemsQueryCacheKey cachingKey, IndexInfo primaryTargetIndex, List resultColumns) { AddResultColumnIndexes(resultColumns, primaryTargetIndex, 0); - var association = cachingKey.ReferencingField.Associations.Last(); + var association = cachingKey.ReferencingField.Associations[^1]; var field = association.Reversed.OwnerField; var keyColumnTypes = field.Columns.SelectToArray(column => column.ValueType); return primaryTargetIndex diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs index ed4300d2f8..4979a275d7 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs @@ -141,7 +141,7 @@ private static bool AreAllForeignKeyColumnsLoaded(EntityState state, FieldInfo f private void RegisterFetchByKnownForeignKey(PrefetchFieldDescriptor referencingFieldDescriptor, EntityState ownerState) { - var association = referencingFieldDescriptor.Field.Associations.Last(); + var association = referencingFieldDescriptor.Field.Associations[^1]; var referencedKeyTuple = association .ExtractForeignKey(ownerState.Type, ownerState.Tuple); var referencedKeyTupleState = referencedKeyTuple.GetFieldStateMap(TupleFieldState.Null); diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs index c47eb67648..5736c4b0ed 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/ReferencedEntityContainer.cs @@ -66,7 +66,7 @@ public void NotifyOwnerAboutKeyWithUnknownType() private Tuple ExtractForeignKeyTuple(EntityState ownerState) { - var association = ReferencingField.Associations.Last(); + var association = ReferencingField.Associations[^1]; var result = association.ExtractForeignKey(ownerState.Type, ownerState.Tuple); var tupleState = result.GetFieldStateMap(TupleFieldState.Null); for (int i = 0; i < result.Count; i++) { @@ -118,7 +118,7 @@ private void FillColumnCollection() public ReferencedEntityContainer(Key ownerKey, PrefetchFieldDescriptor referencingFieldDescriptor, bool isOwnerTypeKnown, PrefetchManager manager) - : base(null, referencingFieldDescriptor.Field.Associations.Last().TargetType, true, manager) + : base(null, referencingFieldDescriptor.Field.Associations[^1].TargetType, true, manager) { ArgumentValidator.EnsureArgumentNotNull(referencingFieldDescriptor, "referencingFieldDescriptor"); ArgumentValidator.EnsureArgumentNotNull(ownerKey, "ownerKey"); diff --git a/Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs b/Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs index 1c9234eff1..4438905395 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/QueryHelper.cs @@ -115,7 +115,7 @@ public static Expression CreateEntitySetQuery(Expression ownerEntity, FieldInfo } var elementType = field.ItemType; - var association = field.Associations.Last(); + var association = field.Associations[^1]; if (association.Multiplicity==Multiplicity.OneToMany) { var targetField = association.TargetType.Fields[association.Reversed.OwnerField.Name]; var whereParameter = Expression.Parameter(elementType, "p"); diff --git a/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs b/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs index cbc3852cc4..3770b8adaf 100644 --- a/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs +++ b/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs @@ -66,7 +66,7 @@ private void MapCompositeKey(OperationExecutionContext context) columnIndex++; } else { - var association = keyField.Associations.Last(); + var association = keyField.Associations[^1]; var componentKeyValue = Tuple.Create(association.TargetType.Key.TupleDescriptor); sourceTuple.CopyTo(componentKeyValue, columnIndex, keyField.MappingInfo.Length); var componentKey = Key.Create(domain, nodeId, association.TargetType.UnderlyingType, diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Paging.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Paging.cs index 831476dadb..faf06a77c8 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Paging.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Paging.cs @@ -131,7 +131,7 @@ private SqlProvider VisitPagingRowNumber(PagingProvider provider) var source = compiledSource.Request.Statement; var queryRef = SqlDml.QueryRef(source); var query = SqlDml.Select(queryRef); - var rowNumberColumn = queryRef.Columns.Last(); + var rowNumberColumn = queryRef.Columns[^1]; query.Columns.AddRange(queryRef.Columns); query.Where = SqlDml.Between(rowNumberColumn, fromParameterBinding.ParameterReference, diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs index 6334b759f3..2a33a0a0e2 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs @@ -528,7 +528,7 @@ protected override SqlProvider VisitRowNumber(RowNumberProvider provider) var query = ExtractSqlSelect(provider, source); var rowNumber = SqlDml.RowNumber(); - query.Columns.Add(rowNumber, header.Columns.Last().Name); + query.Columns.Add(rowNumber, header.Columns[^1].Name); var columns = ExtractColumnExpressions(query); foreach (var order in directionCollection) rowNumber.OrderBy.Add(columns[order.Key], order.Value==Direction.Positive); diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs index b5d436d182..fe2e315199 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs @@ -44,7 +44,7 @@ protected override Provider VisitInclude(IncludeProvider provider) var source = VisitCompilable(provider.Source); var currentMapping = mappings[provider.Source]; - var calulatedColumn = provider.Header.Columns.Last(); + var calulatedColumn = provider.Header.Columns[^1]; mappings[provider] = Merge(currentMapping, EnumerableUtils.One(calulatedColumn.Index)); if (source == provider.Source) { return provider; @@ -326,7 +326,7 @@ protected override Provider VisitRowNumber(RowNumberProvider provider) mappings[provider.Source] = mappings[provider].Where(i => i < sourceLength).ToList(); var newSource = VisitCompilable(provider.Source); var currentMapping = mappings[provider.Source]; - var rowNumberColumn = provider.Header.Columns.Last(); + var rowNumberColumn = provider.Header.Columns[^1]; mappings[provider] = Merge(currentMapping, EnumerableUtils.One(rowNumberColumn.Index)); return newSource == provider.Source ? provider From 9d9fb2fbcd2d367aba5eeeb81bf7730d6edbc873 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 30 Mar 2026 11:10:15 +0500 Subject: [PATCH 51/64] Remove changelog file of 7.1 branch --- ChangeLog/7.1.7_dev.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ChangeLog/7.1.7_dev.txt diff --git a/ChangeLog/7.1.7_dev.txt b/ChangeLog/7.1.7_dev.txt deleted file mode 100644 index e69de29bb2..0000000000 From 8cdffe7ec4ce41a78283e7543d6435482c6789d2 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 7 Apr 2026 12:45:31 +0500 Subject: [PATCH 52/64] Removed unused class --- Orm/Xtensive.Orm/Core/PreconditionValidator.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 Orm/Xtensive.Orm/Core/PreconditionValidator.cs diff --git a/Orm/Xtensive.Orm/Core/PreconditionValidator.cs b/Orm/Xtensive.Orm/Core/PreconditionValidator.cs deleted file mode 100644 index e8f6bc3bbb..0000000000 --- a/Orm/Xtensive.Orm/Core/PreconditionValidator.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -internal static class PreconditionValidator -{ - [return: NotNull] - public static T NotNull( - [NotNull] this T obj, - string message = default, - [CallerArgumentExpression("obj")] - string parameterName = default) - where T : class - { - return obj ?? throw new ArgumentNullException(parameterName, message); - } - -} \ No newline at end of file From 3bdded42087ff796038043e6b70ac8e29dbfa43b Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 7 Apr 2026 17:23:20 +0500 Subject: [PATCH 53/64] Revert "Caching IPreCompiler wrappers for rewriters" This reverts commit 63ebab5cb9804c767e90dc34518d9317803218ee. --- .../Orm/Providers/DomainHandler.cs | 38 ++++++++++++++++--- .../Transformation/ApplyProviderCorrector.cs | 18 +-------- .../Rse/Transformation/OrderingCorrector.cs | 32 ---------------- .../Rse/Transformation/SkipTakeCorrector.cs | 24 +----------- 4 files changed, 35 insertions(+), 77 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs index 2952a9364b..8ad99b7e84 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/DomainHandler.cs @@ -22,6 +22,8 @@ namespace Xtensive.Orm.Providers /// public abstract class DomainHandler : DomainBoundHandler { + private static readonly OrderingCorrector OrderingCorrector = new OrderingCorrector(ResolveOrderingDescriptor); + private Dictionary memberCompilerProviders; /// @@ -91,13 +93,16 @@ protected virtual IPreCompiler CreatePreCompiler(CompilerConfiguration configura { var providerInfo = Handlers.ProviderInfo; + var applyCorrector = new ApplyProviderCorrector( + !providerInfo.Supports(ProviderFeatures.Apply)); + var skipTakeCorrector = new SkipTakeCorrector( + providerInfo.Supports(ProviderFeatures.NativeTake), + providerInfo.Supports(ProviderFeatures.NativeSkip)); return new CompositePreCompiler( - ApplyProviderCorrector.GetOrCreate(!providerInfo.Supports(ProviderFeatures.Apply)), - SkipTakeCorrector.GetOrCreate( - providerInfo.Supports(ProviderFeatures.NativeTake), - providerInfo.Supports(ProviderFeatures.NativeSkip)), + applyCorrector, + skipTakeCorrector, RedundantColumnOptimizer.Instance, - OrderingCorrector.DefaultResolverInstance); + OrderingCorrector); } /// @@ -189,6 +194,29 @@ private void BuildQueryPreprocessors() QueryPreprocessors = ordered ?? throw new InvalidOperationException(Strings.ExCyclicDependencyInQueryPreprocessorGraphIsDetected); } + private static ProviderOrderingDescriptor ResolveOrderingDescriptor(CompilableProvider provider) + { + var isOrderSensitive = provider.Type is ProviderType.Skip + or ProviderType.Take + or ProviderType.Seek + or ProviderType.Paging + or ProviderType.RowNumber; + var preservesOrder = provider.Type is ProviderType.Skip + or ProviderType.Take + or ProviderType.Seek + or ProviderType.Paging + or ProviderType.RowNumber + or ProviderType.Distinct + or ProviderType.Alias; + var isOrderBreaker = provider.Type is ProviderType.Except + or ProviderType.Intersect + or ProviderType.Union + or ProviderType.Concat + or ProviderType.Existence; + var isSorter = provider.Type is ProviderType.Sort or ProviderType.Index; + return new ProviderOrderingDescriptor(isOrderSensitive, preservesOrder, isOrderBreaker, isSorter); + } + #endregion diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs index 7f2f1eb854..d7061d2222 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ApplyProviderCorrector.cs @@ -15,24 +15,8 @@ namespace Xtensive.Orm.Rse.Transformation /// public sealed class ApplyProviderCorrector : IPreCompiler { - private static readonly Lazy exceptionThrowingCorrector = new(() => new ApplyProviderCorrector(true)); - private static readonly Lazy silentCorrector = new(() => new ApplyProviderCorrector(false)); - private readonly bool throwOnCorrectionFault; - /// - /// Gets existing instance or creates new one and cache it. - /// - /// if set to - /// then will be thrown in case of - /// the correction's fault; otherwise the origin - /// will be returned. - public static ApplyProviderCorrector GetOrCreate(bool throwOnCorrectionFault) => - (throwOnCorrectionFault) - ? exceptionThrowingCorrector.Value - : silentCorrector.Value; - - /// public CompilableProvider Process(CompilableProvider rootProvider) { @@ -49,7 +33,7 @@ public CompilableProvider Process(CompilableProvider rootProvider) /// then will be thrown in case of /// the correction's fault; otherwise the origin /// will be returned. - private ApplyProviderCorrector(bool throwOnCorrectionFault) + public ApplyProviderCorrector(bool throwOnCorrectionFault) { this.throwOnCorrectionFault = throwOnCorrectionFault; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs index cfd279e005..5a3a10c7cf 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/OrderingCorrector.cs @@ -18,8 +18,6 @@ namespace Xtensive.Orm.Rse.Transformation [Serializable] public sealed class OrderingCorrector : IPreCompiler { - public static OrderingCorrector DefaultResolverInstance { get; } = new OrderingCorrector(); - private readonly Func orderingDescriptorResolver; /// @@ -28,28 +26,6 @@ CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) return OrderingRewriter.Rewrite(rootProvider, orderingDescriptorResolver); } - private static ProviderOrderingDescriptor ResolveOrderingDescriptor(CompilableProvider provider) - { - var isOrderSensitive = provider.Type is ProviderType.Skip - or ProviderType.Take - or ProviderType.Seek - or ProviderType.Paging - or ProviderType.RowNumber; - var preservesOrder = provider.Type is ProviderType.Skip - or ProviderType.Take - or ProviderType.Seek - or ProviderType.Paging - or ProviderType.RowNumber - or ProviderType.Distinct - or ProviderType.Alias; - var isOrderBreaker = provider.Type is ProviderType.Except - or ProviderType.Intersect - or ProviderType.Union - or ProviderType.Concat - or ProviderType.Existence; - var isSorter = provider.Type is ProviderType.Sort or ProviderType.Index; - return new ProviderOrderingDescriptor(isOrderSensitive, preservesOrder, isOrderBreaker, isSorter); - } // Constructors @@ -62,13 +38,5 @@ public OrderingCorrector(Func or { this.orderingDescriptorResolver = orderingDescriptorResolver ?? throw new ArgumentNullException(nameof(orderingDescriptorResolver)); } - - /// - /// Initializes a new instance of this class with default resolver - /// - public OrderingCorrector() - { - this.orderingDescriptorResolver = ResolveOrderingDescriptor; - } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs index b2d8890dc6..47ab3e89a3 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/SkipTakeCorrector.cs @@ -16,31 +16,9 @@ namespace Xtensive.Orm.Rse.Transformation [Serializable] public sealed class SkipTakeCorrector : IPreCompiler { - private static readonly Lazy fullPagingSupport = new(() => new SkipTakeCorrector(true, true)); - private static readonly Lazy takeOnlySupport = new(() => new SkipTakeCorrector(true, false)); - private static readonly Lazy skipOnlySupport = new(() => new SkipTakeCorrector(false, true)); - private static readonly Lazy noPagingSupport = new(() => new SkipTakeCorrector(false, false)); - private readonly bool takeSupported; private readonly bool skipSupported; - - /// - /// Gets cached instance of create and cache new one - /// - /// Take operation is supported. - /// Skip operation is supported. - /// Cached instance of corrector. - public static SkipTakeCorrector GetOrCreate(bool takeSupported, bool skipSupported) - { - return (takeSupported, skipSupported) switch { - (true, true) => fullPagingSupport.Value, - (false, true) => skipOnlySupport.Value, - (true, false) => takeOnlySupport.Value, - (false, false) => noPagingSupport.Value - }; - } - /// CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) { @@ -50,7 +28,7 @@ CompilableProvider IPreCompiler.Process(CompilableProvider rootProvider) // Constructors - private SkipTakeCorrector(bool takeSupported, bool skipSupported) + public SkipTakeCorrector(bool takeSupported, bool skipSupported) { this.takeSupported = takeSupported; this.skipSupported = skipSupported; From b8175f01b2301bfce336bd9a02cc00632192bdef Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 9 Apr 2026 11:36:03 +0500 Subject: [PATCH 54/64] Add EntitySet tests to check case of complex keys --- .../Storage/EntitySetTest.cs | 395 ++++++++++++++++-- 1 file changed, 355 insertions(+), 40 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests/Storage/EntitySetTest.cs b/Orm/Xtensive.Orm.Tests/Storage/EntitySetTest.cs index 3e9d7f5888..a3192b43bb 100644 --- a/Orm/Xtensive.Orm.Tests/Storage/EntitySetTest.cs +++ b/Orm/Xtensive.Orm.Tests/Storage/EntitySetTest.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// Copyright (C) 2009-2026 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Elena Vakhtina @@ -9,11 +9,8 @@ using System.Linq; using System.Reflection; using NUnit.Framework; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Configuration; -using Xtensive.Orm.Model; -using Xtensive.Orm.Tests; using Xtensive.Orm.Internals; using Xtensive.Orm.Tests.ObjectModel; using Xtensive.Orm.Tests.ObjectModel.ChinookDO; @@ -58,6 +55,100 @@ public class Author : Entity [Field] public EntitySet Books { get; private set; } } + + + [HierarchyRoot] + [KeyGenerator(KeyGeneratorKind.None)] + public class CompositeKeyPublisher : Entity + { + public const int SecondKeyValue = 1; + + [Field, Key(0)] + public Guid Id0 { get; private set; } + + [Field, Key(1)] + public int Id1 { get; private set; } + + [Field] + public EntitySet Books { get; private set; } + + public CompositeKeyPublisher(Session session, Guid id0) + : this(session, id0, SecondKeyValue) + { + } + + public CompositeKeyPublisher(Session session, Guid id0, int id1) + : base(session, id0, id1) + { + } + } + + [HierarchyRoot] + [KeyGenerator(KeyGeneratorKind.None)] + public class CompositeKeyBook : Entity + { + public const int SecondKeyValue = 2; + + [Field, Key(0)] + public Guid Id0 { get; private set; } + + [Field, Key(1)] + public int Id1 { get; private set; } + + [Field, Key(2)] + public int Id2 { get; private set; } + + [Field] + public int Name { get; set; } + + [Field, Association(PairTo = "Books", OnTargetRemove = OnRemoveAction.Clear)] + public CompositeKeyAuthor Author { get; private set; } + + public CompositeKeyBook(Session session, Guid id0) + : this(session, id0, SecondKeyValue, SecondKeyValue) + { + } + + public CompositeKeyBook(Session session, Guid id0, int id1, int id2) + : base(session, id0, id1, id2) + { + } + } + + [HierarchyRoot] + [KeyGenerator(KeyGeneratorKind.None)] + public class CompositeKeyAuthor : Entity + { + public const int SecondKeyValue = 3; + + [Field, Key(0)] + public Guid Id0 { get; private set; } + + [Field, Key(1)] + public int Id1 { get; private set; } + + [Field, Key(2)] + public int Id2 { get; private set; } + + [Field, Key(3)] + public int Id3 { get; private set; } + + [Field] + public int Name { get; set; } + + [Field] + public EntitySet Books { get; private set; } + + public CompositeKeyAuthor(Session session, Guid id0) + : this(session, id0, SecondKeyValue, SecondKeyValue, SecondKeyValue) + { + } + + public CompositeKeyAuthor(Session session, Guid id0, int id1, int id2, int id3) + : base(session, id0, id1, id2, id3) + { + } + } } namespace Xtensive.Orm.Tests.Storage @@ -111,7 +202,7 @@ public void SimpleEntitySetPrefetchTest() var a = new Author(); key = a.Key; for (int i = 0; i < 10; i++) - a.Books.Add(new Book()); + _ = a.Books.Add(new Book()); tx.Complete(); } } @@ -139,7 +230,7 @@ public void AddNewEntityToEntitySetTest() using (var t = session.OpenTransaction()) { var books = a.Books; // fetch the author var b = new Book(); - books.Add(b); + _ = books.Add(b); Assert.That(b.PersistenceState, Is.EqualTo(PersistenceState.New)); t.Complete(); } @@ -155,7 +246,7 @@ public void PairedEntitySetTest() author = new Author(); for (int i = 0; i < 100; i++) { var book = new Book() { Name = i }; - author.Books.Add(book); + _ = author.Books.Add(book); } t.Complete(); } @@ -169,16 +260,37 @@ public void PairedEntitySetTest() } } + [Test] + public void PairedEntitySetTest2() + { + CompositeKeyAuthor author; + using (var session = Domain.OpenSession()) { + using (var t = session.OpenTransaction()) { + author = new CompositeKeyAuthor(session, Guid.NewGuid()); + for (int i = 0; i < 100; i++) { + var book = new CompositeKeyBook(session, Guid.NewGuid()) { Name = i }; + _ = author.Books.Add(book); + } + t.Complete(); + } + using (var t = session.OpenTransaction()) { + var list = author.Books.ToList(); + foreach (var book in list) { + Assert.That(book, Is.Not.Null); + } + } + } + } + [Test] public void NonPairedEntitySetTest() { - using (var session = Domain.OpenSession()) - { + using (var session = Domain.OpenSession()) { using (var t = session.OpenTransaction()) { var publisher = new Publisher(); for (int i = 0; i < 100; i++) { - var book = new Book() {Name = i}; - publisher.Books.Add(book); + var book = new Book() { Name = i }; + _ = publisher.Books.Add(book); } t.Complete(); } @@ -192,6 +304,28 @@ public void NonPairedEntitySetTest() } } + [Test] + public void NonPairedEntitySetTest2() + { + using (var session = Domain.OpenSession()) { + using (var t = session.OpenTransaction()) { + var publisher = new CompositeKeyPublisher(session, Guid.NewGuid()); + for (int i = 0; i < 100; i++) { + var book = new CompositeKeyBook(session, Guid.NewGuid()) { Name = i }; + _ = publisher.Books.Add(book); + } + t.Complete(); + } + using (var t = session.OpenTransaction()) { + var publisher = session.Query.All().First(); + var list = publisher.Books.ToList(); + foreach (var book in list) { + Assert.That(book, Is.Not.Null); + } + } + } + } + [Test] public void OneToManyTest() { @@ -235,12 +369,12 @@ public void NewObjectTest() using (var t = session.OpenTransaction()) { var author = new Author(); for (int i = 0; i < bookCount; i++) - author.Books.Add(new Book {Name = i}); + _ = author.Books.Add(new Book {Name = i}); var book = new Book {Name = bookCount}; - author.Books.Add(book); + _ = author.Books.Add(book); Assert.That(bookCount + 1, Is.EqualTo(author.Books.Count)); - author.Books.Contains(book); - author.Books.Remove(book); + _ = author.Books.Contains(book); + _ = author.Books.Remove(book); Assert.That(bookCount, Is.EqualTo(author.Books.Count)); var enumerator = author.Books.GetEnumerator(); var list = new List(); @@ -253,6 +387,32 @@ public void NewObjectTest() } } + [Test] + public void NewObjectTest2() + { + const int bookCount = 10; + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author = new CompositeKeyAuthor(session, Guid.NewGuid()); + for (int i = 0; i < bookCount; i++) + _ = author.Books.Add(new CompositeKeyBook(session, Guid.NewGuid()) { Name = i }); + var book = new CompositeKeyBook(session, Guid.NewGuid()) { Name = bookCount }; + _ = author.Books.Add(book); + Assert.That(bookCount + 1, Is.EqualTo(author.Books.Count)); + _ = author.Books.Contains(book); + _ = author.Books.Remove(book); + Assert.That(bookCount, Is.EqualTo(author.Books.Count)); + var enumerator = author.Books.GetEnumerator(); + var list = new List(); + while (enumerator.MoveNext()) + list.Add(enumerator.Current); + Assert.That(author.Books.Count, Is.EqualTo(list.Count)); + author.Books.Clear(); + Assert.That(0, Is.EqualTo(author.Books.Count)); + t.Complete(); + } + } + [Test] public void PersistentObjectTest() { @@ -261,10 +421,10 @@ public void PersistentObjectTest() var playlist = session.Query.All().First(); var trackCount = playlist.Tracks.Count; var track = new AudioTrack {Name = "Temp1"}; - playlist.Tracks.Add(track); + _ = playlist.Tracks.Add(track); Assert.That(trackCount + 1, Is.EqualTo(playlist.Tracks.Count)); - playlist.Tracks.Contains(track); - playlist.Tracks.Remove(track); + _ = playlist.Tracks.Contains(track); + _ = playlist.Tracks.Remove(track); Assert.That(trackCount, Is.EqualTo(playlist.Tracks.Count)); var enumerator = playlist.Tracks.GetEnumerator(); var list = new List(); @@ -281,7 +441,7 @@ public void PersistentObjectTest() var category = session.Query.All().First(); Assert.That(0, Is.EqualTo(category.Tracks.Count)); var track = new VideoTrack() {Name = "Temp2"}; - category.Tracks.Add(track); + _ = category.Tracks.Add(track); Session.Current.SaveChanges(); t.Complete(); } @@ -345,6 +505,23 @@ public void EnumerateFullyLoadedEntitySetWhenItsOwnerIsRemovedTest() } } + [Test] + public void EnumerateFullyLoadedEntitySetWhenItsOwnerIsRemovedTest2() + { + Key author0Key; + Key author1Key; + CreateTwoAuthorsAndTheirBooksSet2(out author0Key, out author1Key); + + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author0 = session.Query.Single(author0Key); + LoadEntitySetThenRemoveOwnerAndEnumerateIt2(author0, author0.Books); + + var author1 = session.Query.Single(author1Key); + LoadEntitySetThenRemoveOwnerAndEnumerateIt2(author1, author1.Books); + } + } + [Test] public void EnumerateNotLoadedEntitySetWhenItsOwnerIsRemovedTest() { @@ -362,6 +539,23 @@ public void EnumerateNotLoadedEntitySetWhenItsOwnerIsRemovedTest() } } + [Test] + public void EnumerateNotLoadedEntitySetWhenItsOwnerIsRemovedTest2() + { + Key author0Key; + Key author1Key; + CreateTwoAuthorsAndTheirBooksSet2(out author0Key, out author1Key); + + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author0 = session.Query.Single(author0Key); + RemoveOwnerAndEnumerateEntitySet2(author0, author0.Books); + + var author1 = session.Query.Single(author1Key); + RemoveOwnerAndEnumerateEntitySet2(author1, author1.Books); + } + } + [Test] public void ExecutingFutureQueryOnEntitySetWhenItsOwnerHasBeenRemovedTest() { @@ -388,7 +582,7 @@ public void CountPropertyBehaviorTest() Key smallKey; Action generator = (a, count) => { for (var i = 0; i < count; i++) - a.Books.Add(new Book()); + _ = a.Books.Add(new Book()); }; using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { @@ -402,30 +596,74 @@ public void CountPropertyBehaviorTest() } var booksField = Domain.Model.Types[typeof (Author)].Fields["Books"]; - TestAdd(bigKey, itemCountOfBigEntitySet, booksField); - TestRemove(bigKey, itemCountOfBigEntitySet + 2, booksField); - TestSmallEntitySet(smallKey, itemCountOfSmallEntitySet, booksField); + TestAdd1(bigKey, itemCountOfBigEntitySet, booksField); + TestRemove1(bigKey, itemCountOfBigEntitySet + 2, booksField); + TestSmallEntitySet1(smallKey, itemCountOfSmallEntitySet, booksField); + } + + [Test] + public void CountPropertyBehaviorTest2() + { + const int itemCountOfBigEntitySet = 50; + const int itemCountOfSmallEntitySet = 30; + Key bigKey; + Key smallKey; + Action generator = (a, count) => { + for (var i = 0; i < count; i++) + _ = a.Books.Add(new CompositeKeyBook(a.Session, Guid.NewGuid())); + }; + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var bigAuthor = new CompositeKeyAuthor(session, Guid.NewGuid()); + bigKey = bigAuthor.Key; + generator.Invoke(bigAuthor, itemCountOfBigEntitySet); + var smallAuthor = new CompositeKeyAuthor(session, Guid.NewGuid()); + smallKey = smallAuthor.Key; + generator.Invoke(smallAuthor, itemCountOfSmallEntitySet); + t.Complete(); + } + + var booksField = Domain.Model.Types[typeof(CompositeKeyAuthor)].Fields["Books"]; + TestAdd2(bigKey, itemCountOfBigEntitySet, booksField); + TestRemove2(bigKey, itemCountOfBigEntitySet + 2, booksField); + TestSmallEntitySet2(smallKey, itemCountOfSmallEntitySet, booksField); } - private void TestAdd(Key key, int itemCount, Orm.Model.FieldInfo booksField) + private void TestAdd1(Key key, int itemCount, Orm.Model.FieldInfo booksField) { using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var author = session.Query.Single(key); FetchEntitySet(author.Books); - author.Books.Add(new Book()); - EntitySetState setState; - session.Handler.LookupState(key, booksField, out setState); + _ = author.Books.Add(new Book()); + _ = session.Handler.LookupState(key, booksField, out var setState); + Assert.That(setState.TotalItemCount, Is.Null); + Assert.That(author.Books.Count, Is.EqualTo(itemCount + 1)); + Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 1)); + _ = author.Books.Add(new Book()); + Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 2)); + t.Complete(); + } + } + + private void TestAdd2(Key key, int itemCount, Orm.Model.FieldInfo booksField) + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author = session.Query.Single(key); + FetchEntitySet(author.Books); + _ = author.Books.Add(new CompositeKeyBook(session, Guid.NewGuid())); + _ = session.Handler.LookupState(key, booksField, out var setState); Assert.That(setState.TotalItemCount, Is.Null); Assert.That(author.Books.Count, Is.EqualTo(itemCount + 1)); Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 1)); - author.Books.Add(new Book()); + _ = author.Books.Add(new CompositeKeyBook(session, Guid.NewGuid())); Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 2)); t.Complete(); } } - private void TestRemove(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) + private void TestRemove1(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) { using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { @@ -434,27 +672,58 @@ private void TestRemove(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo boo var booksToBeRemoved = session.Query.All().Where(b => b.Author.Key == key).Take(2).ToList(); var bookToBeRemoved0 = booksToBeRemoved[0]; var bookToBeRemoved1 = booksToBeRemoved[1]; - author.Books.Remove(bookToBeRemoved0); - EntitySetState setState; - session.Handler.LookupState(key, booksField, out setState); + _ = author.Books.Remove(bookToBeRemoved0); + _ = session.Handler.LookupState(key, booksField, out var setState); + Assert.That(setState.TotalItemCount, Is.Null); + Assert.That(author.Books.Count, Is.EqualTo(itemCount - 1)); + Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount - 1)); + _ = author.Books.Remove(bookToBeRemoved1); + Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount - 2)); + t.Complete(); + } + } + + private void TestRemove2(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author = session.Query.Single(key); + FetchEntitySet(author.Books); + var booksToBeRemoved = session.Query.All().Where(b => b.Author.Key == key).Take(2).ToList(); + var bookToBeRemoved0 = booksToBeRemoved[0]; + var bookToBeRemoved1 = booksToBeRemoved[1]; + _ = author.Books.Remove(bookToBeRemoved0); + _ = session.Handler.LookupState(key, booksField, out var setState); Assert.That(setState.TotalItemCount, Is.Null); Assert.That(author.Books.Count, Is.EqualTo(itemCount - 1)); Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount - 1)); - author.Books.Remove(bookToBeRemoved1); + _ = author.Books.Remove(bookToBeRemoved1); Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount - 2)); t.Complete(); } } - private void TestSmallEntitySet(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) + private void TestSmallEntitySet1(Key key, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) { using (var session = Domain.OpenSession()) using (var t = session.OpenTransaction()) { var author = session.Query.Single(key); FetchEntitySet(author.Books); - author.Books.Add(new Book()); + _ = author.Books.Add(new Book()); + Assert.That(session.Handler.LookupState(key, booksField, out var setState), Is.True); + Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 1)); + } + } + + private void TestSmallEntitySet2(Key searchKey, int itemCount, Xtensive.Orm.Model.FieldInfo booksField) + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + var author = session.Query.Single(searchKey); + FetchEntitySet(author.Books); + _ = author.Books.Add(new CompositeKeyBook(session, Guid.NewGuid())); EntitySetState setState; - Assert.That(session.Handler.LookupState(key, booksField, out setState), Is.True); + Assert.That(session.Handler.LookupState(searchKey, booksField, out setState), Is.True); Assert.That(setState.TotalItemCount, Is.EqualTo(itemCount + 1)); } } @@ -465,11 +734,24 @@ private static void LoadEntitySetThenRemoveOwnerAndEnumerateIt(Author owner, Ent RemoveOwnerAndEnumerateEntitySet(owner, entitySet); } + private static void LoadEntitySetThenRemoveOwnerAndEnumerateIt2(CompositeKeyAuthor owner, EntitySet entitySet) + { + foreach (var book in entitySet) { } + RemoveOwnerAndEnumerateEntitySet2(owner, entitySet); + } + private static void RemoveOwnerAndEnumerateEntitySet(Author owner, EntitySet entitySet) { var expectedCount = entitySet.Count; owner.Remove(); - entitySet.GetEnumerator().MoveNext(); + _ = entitySet.GetEnumerator().MoveNext(); + } + + private static void RemoveOwnerAndEnumerateEntitySet2(CompositeKeyAuthor owner, EntitySet entitySet) + { + var expectedCount = entitySet.Count; + owner.Remove(); + _ = entitySet.GetEnumerator().MoveNext(); } private void CreateTwoAuthorsAndTheirBooksSet(out Key author0Key, out Key author1Key) @@ -478,7 +760,7 @@ private void CreateTwoAuthorsAndTheirBooksSet(out Key author0Key, out Key author using (var t = session.OpenTransaction()) { Action bookGenerator = (author, count) => { for (var i = 0; i < count; i++) - author.Books.Add(new Book()); + _ = author.Books.Add(new Book()); }; var author0 = new Author(); author0Key = author0.Key; @@ -490,6 +772,24 @@ private void CreateTwoAuthorsAndTheirBooksSet(out Key author0Key, out Key author } } + private void CreateTwoAuthorsAndTheirBooksSet2(out Key author0Key, out Key author1Key) + { + using (var session = Domain.OpenSession()) + using (var t = session.OpenTransaction()) { + Action bookGenerator = (author, count) => { + for (var i = 0; i < count; i++) + _ = author.Books.Add(new CompositeKeyBook(session, Guid.NewGuid())); + }; + var author0 = new CompositeKeyAuthor(session, Guid.NewGuid()); + author0Key = author0.Key; + bookGenerator.Invoke(author0, 5); + var author1 = new CompositeKeyAuthor(session, Guid.NewGuid()); + author1Key = author1.Key; + bookGenerator.Invoke(author1, 50); + t.Complete(); + } + } + private List GenerateInvoices(int count) { var result = new List(); @@ -500,8 +800,23 @@ private List GenerateInvoices(int count) private void FetchEntitySet(EntitySet books) where T : IEntity { - // fancy trick to force loading at most N items (currently N = 32) - books.Contains(Key.Create(Domain, typeof (T), -77)); + var keyFieldCount = books.Session.Domain.Model.Types[typeof(T)].Key.TupleDescriptor.Count; + if (keyFieldCount == 1) { + // fancy trick to force loading at most N items (currently N = 32) + _ = books.Contains(Key.Create(Domain, typeof(T), -77)); + } + else if (keyFieldCount == 2) { + // fancy trick to force loading at most N items (currently N = 32) + _ = books.Contains(Key.Create(Domain, typeof(T), Guid.NewGuid(), -77)); + } + else if (keyFieldCount == 3) { + // fancy trick to force loading at most N items (currently N = 32) + _ = books.Contains(Key.Create(Domain, typeof(T), Guid.NewGuid(), -77, -66)); + } + else if (keyFieldCount == 4) { + // fancy trick to force loading at most N items (currently N = 32) + _ = books.Contains(Key.Create(Domain, typeof(T), Guid.NewGuid(), -77, -66, -55)); + } } } } \ No newline at end of file From 89af35b8d12c0cdc367a1ec622803162f17b77a2 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 9 Apr 2026 15:27:27 +0500 Subject: [PATCH 55/64] No TupleDescriptor.FieldTypes array usage from outside the type --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 20 +--- .../Tuples/Transform/ConcatTransform.cs | 7 +- .../Tuples/Transform/SegmentTransform.cs | 4 +- Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs | 92 +++++++++++++------ 4 files changed, 69 insertions(+), 54 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 871bb6cfcd..2434ae9c12 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -956,33 +956,23 @@ private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, Entit var targetDescriptor = association.TargetType.Key.TupleDescriptor; var ownerFieldCount = ownerDescriptor.Count; - Type[] keyFieldTypes; IReadOnlyList itemColumnOffsets; if (association.AuxiliaryType == null) { - itemColumnOffsets = Enumerable.Repeat(-1, ownerFieldCount).Concat( - association.UnderlyingIndex.ValueColumns + itemColumnOffsets = Enumerable.Repeat(-1, ownerFieldCount) + .Concat(association.UnderlyingIndex.ValueColumns .Where(ci => ci.IsPrimaryKey) .Select(ci => ci.Field.MappingInfo.Offset)).ToList(); - var keyFieldCount = itemColumnOffsets.Count; - keyFieldTypes = new Type[keyFieldCount]; - Array.Copy(ownerDescriptor.FieldTypes, keyFieldTypes, ownerFieldCount); - for (var index=ownerFieldCount; index itemCtor = null; if (association.AuxiliaryType != null) { diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index 03802e436f..e1ef2e3ba8 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -74,13 +74,8 @@ public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor s ArgumentValidator.EnsureArgumentIsNotDefault(first, nameof(first)); ArgumentValidator.EnsureArgumentIsNotDefault(second, nameof(second)); - var (firstCount, secondCount) = (first.Count, second.Count); - var types = new Type[firstCount + secondCount]; - Array.Copy(first.FieldTypes, types, firstCount); - Array.Copy(second.FieldTypes, 0, types, firstCount, secondCount); - IsReadOnly = isReadOnly; - Descriptor = TupleDescriptor.Create(types); + Descriptor = first.ConcatWith(second); this.sources = (first, second); } } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 90e35fa144..0d1a2d161b 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -79,9 +79,7 @@ public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Se IsReadOnly = isReadOnly; - var fields = new Type[segment.Length]; - Array.Copy(sourceDescriptor.FieldTypes, segment.Offset, fields, 0, segment.Length); - Descriptor = TupleDescriptor.Create(fields); + Descriptor = sourceDescriptor.Segment(segment); this.segment = segment; } } diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 05ff91798c..349531ec5d 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs @@ -34,9 +34,9 @@ namespace Xtensive.Tuples internal readonly PackedFieldDescriptor[] FieldDescriptors; [field: NonSerialized] - internal Type[] FieldTypes { get; } + private Type[] FieldTypes { get; } - #region IList members + #region IReadOnlyList members /// public Type this[int fieldIndex] @@ -69,6 +69,66 @@ IEnumerator IEnumerable.GetEnumerator() #endregion + /// + /// Creates tuple descriptor containing head of the current one. + /// + /// Head field count. + /// + /// New tuple descriptor describing the specified set of fields. + /// + public TupleDescriptor Head(int fieldCount) + { + ArgumentValidator.EnsureArgumentIsInRange(fieldCount, 1, Count, nameof(fieldCount)); + var fieldTypes = new Type[fieldCount]; + Array.Copy(FieldTypes, 0, fieldTypes, 0, fieldCount); + return new TupleDescriptor(fieldTypes); + } + + /// + /// Creates tuple descriptor containing tail of the current one. + /// + /// Tail field count. + /// + /// New tuple descriptor describing the specified set of fields. + /// + public TupleDescriptor Tail(int tailFieldCount) + { + ArgumentValidator.EnsureArgumentIsInRange(tailFieldCount, 1, Count, nameof(tailFieldCount)); + var fieldTypes = new Type[tailFieldCount]; + Array.Copy(FieldTypes, Count - tailFieldCount, fieldTypes, 0, tailFieldCount); + return new TupleDescriptor(fieldTypes); + } + + /// + /// Creates tuple descriptor containing segment of the current one + /// + /// Offset and length of segment in form of Segment + /// + /// New tuple descriptor describing the specified set of fields. + /// + public TupleDescriptor Segment(in Segment segment) + { + var fields = new Type[segment.Length]; + Array.Copy(FieldTypes, segment.Offset, fields, 0, segment.Length); + + return new TupleDescriptor(fields); + } + + /// + /// Concats fields of the current and the given descriptors to form new one. + /// + /// Tail fields descriptor. + /// New tuple descriptor containing fields of the both given source descriptors. + public TupleDescriptor ConcatWith(in TupleDescriptor second) + { + var (firstCount, secondCount) = (Count, second.Count); + var types = new Type[firstCount + secondCount]; + Array.Copy(FieldTypes, types, firstCount); + Array.Copy(second.FieldTypes, 0, types, firstCount, secondCount); + + return new TupleDescriptor(types); + } + #region IEquatable members, GetHashCode /// @@ -177,34 +237,6 @@ public static TupleDescriptor Create(Type[] fieldTypes) return new TupleDescriptor(fieldTypes); } - /// - /// Creates tuple descriptor containing head of the current one. - /// - /// Head field count. - /// Either new or existing tuple descriptor - /// describing the specified set of fields. - public TupleDescriptor Head(int fieldCount) - { - ArgumentValidator.EnsureArgumentIsInRange(fieldCount, 1, Count, nameof(fieldCount)); - var fieldTypes = new Type[fieldCount]; - Array.Copy(FieldTypes, 0, fieldTypes, 0, fieldCount); - return new TupleDescriptor(fieldTypes); - } - - /// - /// Creates tuple descriptor containing tail of the current one. - /// - /// Tail field count. - /// Either new or existing tuple descriptor - /// describing the specified set of fields. - public TupleDescriptor Tail(int tailFieldCount) - { - ArgumentValidator.EnsureArgumentIsInRange(tailFieldCount, 1, Count, nameof(tailFieldCount)); - var fieldTypes = new Type[tailFieldCount]; - Array.Copy(FieldTypes, Count - tailFieldCount, fieldTypes, 0, tailFieldCount); - return new TupleDescriptor(fieldTypes); - } - #endregion #region Create<...> methods (generic shortcuts) From e8e9b336e124a7f5bf86c3908a9b0c1fb78feb05 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 13 Apr 2026 18:03:03 +0500 Subject: [PATCH 56/64] Add some tests for SegmentTransform and ConcatTransform - Performance tests of constructors - ToString test for ConcatTransform --- .../Tuples/Transform/MergeTransformTest.cs | 85 +++++++++++++++++-- .../Tuples/Transform/SegmentTransformTest.cs | 78 +++++++++++++---- 2 files changed, 141 insertions(+), 22 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs index b506c535cd..f7a11e5168 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs @@ -14,7 +14,8 @@ namespace Xtensive.Orm.Tests.Core.Tuples.Transform [TestFixture] public class MergeTransformTest { - public const int IterationCount = 1000000; + private const int IterationCount = 1_000_000; + private const int MeasurementRuns = 5; [Test] public void BaseTest() @@ -55,10 +56,21 @@ public void BaseTest() }); } + [Test] + public void ToStringTest() + { + Xtensive.Tuples.Tuple t1 = Xtensive.Tuples.Tuple.Create(1, "2"); + Xtensive.Tuples.Tuple t2 = Xtensive.Tuples.Tuple.Create(3, 4.0, "5"); + + var ct = new ConcatTransform(false, t1.Descriptor, t2.Descriptor); + Assert.That(ct.ToString(), Is.EqualTo("ConcatTransform(TupleDescriptor(Int32, String) + TupleDescriptor(Int32, Double, String), r/w)")); + + } + [Test] [Explicit] [Category("Performance")] - public void PerformanceTest1() + public void ComparisonPerformanceTest() { AdvancedComparerStruct comparer = AdvancedComparerStruct.Default; Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1); @@ -74,19 +86,78 @@ public void PerformanceTest1() comparer.Equals(wt1, wt2); TestHelper.CollectGarbage(); - using (new Measurement("O&O", MeasurementOptions.Log, count)) - for (int i = 0; i comparer = AdvancedComparerStruct.Default; Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1, 2, 3, 4); @@ -69,24 +70,71 @@ public void PerformanceTest() Xtensive.Tuples.Tuple ct2 = st.Apply(TupleTransformType.Tuple, t); int count = IterationCount; - comparer.Equals(ct1, ct2); - comparer.Equals(ct1, wt1); - comparer.Equals(wt1, wt2); + _ = comparer.Equals(ct1, ct2); + _ = comparer.Equals(ct1, wt1); + _ = comparer.Equals(wt1, wt2); TestHelper.CollectGarbage(); - using (new Measurement("O&O", MeasurementOptions.Log, count)) - for (int i = 0; i(1, 1)); + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 2)); + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 3)); + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 4)); + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 5)); + + for (var run = 0; run < MeasurementRuns; run++) { + TestHelper.CollectGarbage(); + using (var mx = new Measurement("S1|1", MeasurementOptions.Log, count)) { + for (int i = 0; i < count; i++) { + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 1)); + } + + mx.Complete(); + Console.WriteLine(mx.ToString()); + } + } + + Console.WriteLine(); + for (var run = 0; run < MeasurementRuns; run++) { + TestHelper.CollectGarbage(); + using (var mx = new Measurement("S1|5", MeasurementOptions.Log, count)) { + for (int i = 0; i < count; i++) { + _ = new SegmentTransform(false, t.Descriptor, new Segment(1, 5)); + } + + mx.Complete(); + Console.WriteLine(mx.ToString()); + } + } } } } \ No newline at end of file From eff88976a4cd0f4477fadf8936ebb68e26f275f7 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Mon, 13 Apr 2026 18:05:01 +0500 Subject: [PATCH 57/64] ConcatTransform doesn't hold original descriptors just for ToString() opertion --- .../Tuples/Transform/ConcatTransform.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index e1ef2e3ba8..c1d6e8c7d3 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -17,7 +17,7 @@ namespace Xtensive.Tuples.Transform [Serializable] public sealed class ConcatTransform { - private readonly (TupleDescriptor first, TupleDescriptor second) sources; + private readonly (int first, int second) sourceParts; /// public TupleDescriptor Descriptor { get; } @@ -54,7 +54,23 @@ Tuple CopySourceTuples(Tuple source1, Tuple source2) { /// public override string ToString() { - var description = $"{sources.first} + {sources.second}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + var sb = new ValueStringBuilder(stackalloc char[4096]); + for (int i = 0, count = sourceParts.first; i < count; i++) { + if (i > 0) + sb.Append(", "); + sb.Append(Descriptor[i].GetShortName()); + } + var sourceOne = string.Format(Strings.TupleDescriptorFormat, sb.ToString()); + + sb = new ValueStringBuilder(stackalloc char[4096]); + for (int i = sourceParts.first, count = Descriptor.Count; i < count; i++) { + if (i > sourceParts.first) + sb.Append(", "); + sb.Append(Descriptor[i].GetShortName()); + } + var sourceTwo = string.Format(Strings.TupleDescriptorFormat, sb.ToString()); + + var description = $"{sourceOne} + {sourceTwo}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; return string.Format(Strings.TupleTransformFormat, nameof(ConcatTransform), description); @@ -76,7 +92,7 @@ public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor s IsReadOnly = isReadOnly; Descriptor = first.ConcatWith(second); - this.sources = (first, second); + this.sourceParts = (first.Count, second.Count); } } } \ No newline at end of file From 961f37f11c18cf7d93391dc218f43695be84648a Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 14 Apr 2026 10:58:01 +0500 Subject: [PATCH 58/64] Made TupleDescriptor's fieldTypes readonly field --- Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs | 65 +++++++++++----------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 349531ec5d..260d7d2225 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs @@ -32,31 +32,30 @@ namespace Xtensive.Tuples [NonSerialized] internal readonly PackedFieldDescriptor[] FieldDescriptors; - - [field: NonSerialized] - private Type[] FieldTypes { get; } + + [NonSerialized] + private readonly Type[] fieldTypes; #region IReadOnlyList members /// public Type this[int fieldIndex] { - get => FieldTypes[fieldIndex]; - set => throw Exceptions.CollectionIsReadOnly(null); + get => fieldTypes[fieldIndex]; } /// public int Count { [DebuggerStepThrough] - get => FieldTypes.Length; + get => fieldTypes.Length; } /// public IEnumerator GetEnumerator() { for (int index = 0, count = Count; index < count; index++) { - yield return FieldTypes[index]; + yield return fieldTypes[index]; } } @@ -80,7 +79,7 @@ public TupleDescriptor Head(int fieldCount) { ArgumentValidator.EnsureArgumentIsInRange(fieldCount, 1, Count, nameof(fieldCount)); var fieldTypes = new Type[fieldCount]; - Array.Copy(FieldTypes, 0, fieldTypes, 0, fieldCount); + Array.Copy(this.fieldTypes, 0, fieldTypes, 0, fieldCount); return new TupleDescriptor(fieldTypes); } @@ -95,7 +94,7 @@ public TupleDescriptor Tail(int tailFieldCount) { ArgumentValidator.EnsureArgumentIsInRange(tailFieldCount, 1, Count, nameof(tailFieldCount)); var fieldTypes = new Type[tailFieldCount]; - Array.Copy(FieldTypes, Count - tailFieldCount, fieldTypes, 0, tailFieldCount); + Array.Copy(this.fieldTypes, Count - tailFieldCount, fieldTypes, 0, tailFieldCount); return new TupleDescriptor(fieldTypes); } @@ -108,10 +107,10 @@ public TupleDescriptor Tail(int tailFieldCount) /// public TupleDescriptor Segment(in Segment segment) { - var fields = new Type[segment.Length]; - Array.Copy(FieldTypes, segment.Offset, fields, 0, segment.Length); + var fieldTypes = new Type[segment.Length]; + Array.Copy(this.fieldTypes, segment.Offset, fieldTypes, 0, segment.Length); - return new TupleDescriptor(fields); + return new TupleDescriptor(fieldTypes); } /// @@ -122,11 +121,11 @@ public TupleDescriptor Segment(in Segment segment) public TupleDescriptor ConcatWith(in TupleDescriptor second) { var (firstCount, secondCount) = (Count, second.Count); - var types = new Type[firstCount + secondCount]; - Array.Copy(FieldTypes, types, firstCount); - Array.Copy(second.FieldTypes, 0, types, firstCount, secondCount); + var fieldTypes = new Type[firstCount + secondCount]; + Array.Copy(this.fieldTypes, fieldTypes, firstCount); + Array.Copy(second.fieldTypes, 0, fieldTypes, firstCount, secondCount); - return new TupleDescriptor(types); + return new TupleDescriptor(fieldTypes); } #region IEquatable members, GetHashCode @@ -134,15 +133,15 @@ public TupleDescriptor ConcatWith(in TupleDescriptor second) /// public bool Equals(TupleDescriptor other) { - if (FieldTypes == null) { - return other.FieldTypes == null; + if (fieldTypes == null) { + return other.fieldTypes == null; } - if (other.FieldTypes == null || Count != other.Count) { + if (other.fieldTypes == null || Count != other.Count) { return false; } for (int i = 0, count = Count; i < count; i++) { - if (FieldTypes[i] != other.FieldTypes[i]) { + if (fieldTypes[i] != other.fieldTypes[i]) { return false; } } @@ -158,7 +157,7 @@ public override int GetHashCode() { int result = Count; for (int i = 0, count = Count; i < count; i++) - result = unchecked (FieldTypes[i].GetHashCode() + 29 * result); + result = unchecked (fieldTypes[i].GetHashCode() + 29 * result); return result; } @@ -179,11 +178,11 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue(nameof(ValuesLength), ValuesLength); info.AddValue(nameof(ObjectsLength), ObjectsLength); - var typeNames = new string[FieldTypes.Length]; + var typeNames = new string[fieldTypes.Length]; for (var i = 0; i < typeNames.Length; i++) - typeNames[i] = FieldTypes[i].ToSerializableForm(); + typeNames[i] = fieldTypes[i].ToSerializableForm(); - info.AddValue(nameof(FieldTypes), typeNames); + info.AddValue(nameof(fieldTypes), typeNames); info.AddValue(nameof(FieldDescriptors), FieldDescriptors); } @@ -194,7 +193,7 @@ public override string ToString() for (int i = 0, count = Count; i < count; i++) { if (i > 0) sb.Append(", "); - sb.Append(FieldTypes[i].GetShortName()); + sb.Append(fieldTypes[i].GetShortName()); } return string.Format(Strings.TupleDescriptorFormat, sb.ToString()); } @@ -286,7 +285,7 @@ public static TupleDescriptor Create() private TupleDescriptor(Type[] fieldTypes) { var fieldCount = fieldTypes.Length; - FieldTypes = fieldTypes; + this.fieldTypes = fieldTypes; FieldDescriptors = new PackedFieldDescriptor[fieldCount]; switch (fieldCount) { @@ -295,17 +294,17 @@ private TupleDescriptor(Type[] fieldTypes) ObjectsLength = 0; return; case 1: - TupleLayout.ConfigureLen1(ref FieldTypes[0], + TupleLayout.ConfigureLen1(ref this.fieldTypes[0], ref FieldDescriptors[0], out ValuesLength, out ObjectsLength); break; case 2: - TupleLayout.ConfigureLen2(FieldTypes, + TupleLayout.ConfigureLen2(this.fieldTypes, ref FieldDescriptors[0], ref FieldDescriptors[1], out ValuesLength, out ObjectsLength); break; default: - TupleLayout.Configure(FieldTypes, FieldDescriptors, out ValuesLength, out ObjectsLength); + TupleLayout.Configure(this.fieldTypes, FieldDescriptors, out ValuesLength, out ObjectsLength); break; } } @@ -315,14 +314,14 @@ public TupleDescriptor(SerializationInfo info, StreamingContext context) ValuesLength = info.GetInt32(nameof(ValuesLength)); ObjectsLength = info.GetInt32(nameof(ObjectsLength)); - var typeNames = (string[]) info.GetValue(nameof(FieldTypes), typeof(string[])); + var typeNames = (string[]) info.GetValue(nameof(fieldTypes), typeof(string[])); FieldDescriptors = (PackedFieldDescriptor[])info.GetValue( nameof(FieldDescriptors), typeof(PackedFieldDescriptor[])); - FieldTypes = new Type[typeNames.Length]; + fieldTypes = new Type[typeNames.Length]; for (var i = 0; i < typeNames.Length; i++) { - FieldTypes[i] = typeNames[i].GetTypeFromSerializableForm(); - TupleLayout.ConfigureFieldAccessor(ref FieldDescriptors[i], FieldTypes[i]); + fieldTypes[i] = typeNames[i].GetTypeFromSerializableForm(); + TupleLayout.ConfigureFieldAccessor(ref FieldDescriptors[i], fieldTypes[i]); } } } From cf82f3c085d001ec0eb6e76f66556459ea993c12 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Tue, 14 Apr 2026 13:05:39 +0500 Subject: [PATCH 59/64] No struct checks for default for internal usage - everywhere inside the project transformations are perforemed on valid descriptors so we can skip checks of structs, transformations are public though so we have to keep validation in public ctor --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 1 - Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs | 4 ++-- .../Materialization/MaterializationContext.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 2 +- .../Transformation/RedundantColumnRemover.cs | 2 +- .../Tuples/Transform/ConcatTransform.cs | 21 +++++++++++++++++-- .../Tuples/Transform/MapTransform.cs | 19 ++++++++++++++++- .../Tuples/Transform/SegmentTransform.cs | 15 ++++++++++++- Orm/Xtensive.Orm/Tuples/TupleExtensions.cs | 4 ++-- 9 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 2434ae9c12..6f52fa050d 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -1055,7 +1055,6 @@ protected EntitySetBase(Entity owner, FieldInfo field) var domain = Session.Domain; var itemType = domain.Model.Types[Field.ItemType]; auxilaryTypeKeyTransform = new ConcatTransform( - false, owner.TypeInfo.Key.TupleDescriptor, itemType.Key.TupleDescriptor); } diff --git a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs index dc10cd6f82..78a19d60a8 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2020 Xtensive LLC. +// Copyright (C) 2014-2020 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexey Kulakov @@ -53,7 +53,7 @@ private void RemapEntitySetReference(RemapContext context, ReferenceFieldChangeI var fieldOwnerKey = context.TryRemapKey(info.FieldOwner); var fieldValueKey = context.TryRemapKey(info.FieldValue); - var transformer = new ConcatTransform(false, fieldOwnerKey.Value.Descriptor, fieldValueKey.Value.Descriptor); + var transformer = new ConcatTransform(fieldOwnerKey.Value.Descriptor, fieldValueKey.Value.Descriptor); var combinedTuple = transformer.Apply(TupleTransformType.Tuple, fieldOwnerKey.Value, fieldValueKey.Value); var newCombinedKey = Key.Create(Session.Domain, Session.StorageNodeId, fieldAssociation.AuxiliaryType, TypeReferenceAccuracy.ExactType, combinedTuple); diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs index bc2f6cc3b6..5d2a44ac72 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs @@ -92,8 +92,8 @@ public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int ArraySegment allIndexes = MaterializationHelper.CreateSingleSourceMap(descriptor.Count, typeColumnMap); ArraySegment keyIndexes = allIndexes.Slice(0, keyInfo.TupleDescriptor.Count); - var transform = new MapTransform(true, descriptor, allIndexes); - var keyTransform = new MapTransform(true, keyInfo.TupleDescriptor, keyIndexes); + var transform = new MapTransform(descriptor, allIndexes); + var keyTransform = new MapTransform(keyInfo.TupleDescriptor, keyIndexes); result = new TypeMapping(type, keyTransform, transform, keyIndexes); diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index 4fd7606930..9940dbae41 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -699,7 +699,7 @@ private void CreateMappingInfo() if (IsEntity || IsStructure) { valueExtractor = new SegmentTransform( - false, reflectedType.TupleDescriptor, new Segment(mappingInfo.Offset, mappingInfo.Length)); + reflectedType.TupleDescriptor, new Segment(mappingInfo.Offset, mappingInfo.Length)); } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs index b9a2c9ef5f..19dc144abb 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs @@ -46,7 +46,7 @@ protected override RawProvider VisitRaw(RawProvider provider) var mapping = mappings[provider]; if (mapping.SequenceEqual(Enumerable.Range(0, provider.Header.Length))) return provider; - var mappingTransform = new MapTransform(true, provider.Header.TupleDescriptor, mapping); + var mappingTransform = new MapTransform(provider.Header.TupleDescriptor, mapping); var newExpression = RemapRawProviderSource(provider.Source, mappingTransform); return new RawProvider(provider.Header.Select(mapping), newExpression); } diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs index c1d6e8c7d3..3b8974e797 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -87,12 +87,29 @@ public override string ToString() /// The of the second source . public ConcatTransform(bool isReadOnly, TupleDescriptor first, TupleDescriptor second) { - ArgumentValidator.EnsureArgumentIsNotDefault(first, nameof(first)); - ArgumentValidator.EnsureArgumentIsNotDefault(second, nameof(second)); + if (first == default) + throw new ArgumentException("Argument is default instance.", nameof(first)); + + if (second == default) + throw new ArgumentException("Argument is default instance.", nameof(second)); IsReadOnly = isReadOnly; Descriptor = first.ConcatWith(second); this.sourceParts = (first.Count, second.Count); } + + + /// + /// Initializes a new instance of this type. + /// + /// The of the first source . + /// The of the second source . + // WARNING !!!!! NO CHECKS FOR DEFAULT VALUES FOR THE SAKE OF PEFRORMANCE + internal ConcatTransform(TupleDescriptor first, TupleDescriptor second) + { + IsReadOnly = false; + Descriptor = first.ConcatWith(second); + this.sourceParts = (first.Count, second.Count); + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs index 056eeab86d..d27152adc6 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -81,12 +81,29 @@ public override string ToString() /// property value. public MapTransform(bool isReadOnly, TupleDescriptor descriptor, IReadOnlyList map) { - ArgumentValidator.EnsureArgumentIsNotDefault(descriptor, nameof(descriptor)); + if (descriptor == default) + throw new ArgumentException("Argument is default instance.",nameof(descriptor)); IsReadOnly = isReadOnly; Descriptor = descriptor; this.map = map ?? throw new ArgumentNullException(nameof(map)); } + + + + /// + /// Initializes a new instance of this type with = . + /// + /// The of the target . + /// property value. + // BE CAREFUL, NO VALIDATION OF PARAMETERS for the sake of performance + internal MapTransform(TupleDescriptor descriptor, IReadOnlyList map) + { + IsReadOnly = true; + Descriptor = descriptor; + + this.map = map ?? throw new ArgumentNullException(nameof(map)); + } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs index 0d1a2d161b..bf1faea3bd 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -75,10 +75,23 @@ public override string ToString() /// The segment to extract. public SegmentTransform(bool isReadOnly, TupleDescriptor sourceDescriptor, in Segment segment) { - ArgumentValidator.EnsureArgumentIsNotDefault(sourceDescriptor, nameof(sourceDescriptor)); + if (sourceDescriptor == default) + throw new ArgumentException("Argument is default instance.", nameof(sourceDescriptor)); IsReadOnly = isReadOnly; + Descriptor = sourceDescriptor.Segment(segment); + this.segment = segment; + } + /// + /// Initializes a new instance of this type. + /// + /// The of the source . + /// The segment to extract. + // WARNING !!!!! NO CHECKS FOR DEFAULT VALUES FOR THE SAKE OF PEFRORMANCE + internal SegmentTransform(TupleDescriptor sourceDescriptor, in Segment segment) + { + IsReadOnly = false; Descriptor = sourceDescriptor.Segment(segment); this.segment = segment; } diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index c9b0cc450a..2c362192ab 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs @@ -115,7 +115,7 @@ public static void CopyTo(this Tuple source, Tuple target, IReadOnlyList ma /// public static Tuple Concat(this Tuple left, Tuple right) { - var transform = new ConcatTransform(false, left.Descriptor, right.Descriptor); + var transform = new ConcatTransform(left.Descriptor, right.Descriptor); return transform.Apply(TupleTransformType.TransformedTuple, left, right); } @@ -127,7 +127,7 @@ public static Tuple Concat(this Tuple left, Tuple right) /// public static Tuple GetSegment(this Tuple tuple, in Segment segment) { - var transform = new SegmentTransform(false, tuple.Descriptor, segment); + var transform = new SegmentTransform(tuple.Descriptor, segment); return transform.Apply(TupleTransformType.TransformedTuple, tuple); } From 0201d463dbcba27ad33862308537efde5c6ce9c9 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Wed, 15 Apr 2026 13:03:30 +0500 Subject: [PATCH 60/64] Avoid complicated arithmetics API for basic operation --- Orm/Xtensive.Orm/Orm/Internals/TupleExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/TupleExtensions.cs b/Orm/Xtensive.Orm/Orm/Internals/TupleExtensions.cs index 9c824c6189..1618bb3529 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/TupleExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/TupleExtensions.cs @@ -35,7 +35,7 @@ public static bool ContainsNonEmptyValues(this Tuple target) public static bool ContainsEmptyValues(this Tuple target, in Segment segment) { - for (int i = segment.Offset; i < segment.EndOffset; i++) { + for (int i = segment.Offset, endOffset = segment.Offset + segment.Length; i < endOffset; i++) { var state = target.GetFieldState(i); if (!state.HasValue()) return true; @@ -45,7 +45,7 @@ public static bool ContainsEmptyValues(this Tuple target, in Segment segmen public static bool ContainsNonEmptyValues(this Tuple target, in Segment segment) { - for (int i = segment.Offset; i < segment.EndOffset; i++) { + for (int i = segment.Offset, endOffset = segment.Offset + segment.Length; i < endOffset; i++) { var state = target.GetFieldState(i); if (state.HasValue()) return true; @@ -55,7 +55,7 @@ public static bool ContainsNonEmptyValues(this Tuple target, in Segment seg public static bool AreAllColumnsAvalilable(this Tuple target, in Segment segment) { - for (int i = segment.Offset; i < segment.EndOffset; i++) { + for (int i = segment.Offset, endOffset = segment.Offset + segment.Length; i < endOffset; i++) { var state = target.GetFieldState(i); if (!state.IsAvailable()) return false; @@ -65,7 +65,7 @@ public static bool AreAllColumnsAvalilable(this Tuple target, in Segment se public static bool IsAtLeastOneColumAvailable(this Tuple target, in Segment segment) { - for (int i = segment.Offset; i < segment.EndOffset; i++) { + for (int i = segment.Offset, endOffset = segment.Offset + segment.Length; i < endOffset; i++) { var state = target.GetFieldState(i); if (state.IsAvailable()) return true; From c4f82ad4ae4def5b3433fe65815e7aec76c082ed Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 16 Apr 2026 17:54:32 +0500 Subject: [PATCH 61/64] Use direct lengths of array instead of Count property --- Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 260d7d2225..28659f8a1d 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs @@ -120,10 +120,11 @@ public TupleDescriptor Segment(in Segment segment) /// New tuple descriptor containing fields of the both given source descriptors. public TupleDescriptor ConcatWith(in TupleDescriptor second) { - var (firstCount, secondCount) = (Count, second.Count); - var fieldTypes = new Type[firstCount + secondCount]; - Array.Copy(this.fieldTypes, fieldTypes, firstCount); - Array.Copy(second.fieldTypes, 0, fieldTypes, firstCount, secondCount); + var firstLength = this.fieldTypes.Length; + var secondLength = second.fieldTypes.Length; + var fieldTypes = new Type[firstLength + secondLength]; + Array.Copy(this.fieldTypes, fieldTypes, firstLength); + Array.Copy(second.fieldTypes, 0, fieldTypes, firstLength, secondLength); return new TupleDescriptor(fieldTypes); } From 7728009f093c5064d25725c12d3ccf5b098a420e Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 16 Apr 2026 19:40:29 +0500 Subject: [PATCH 62/64] A bit faster parameter validation --- Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs | 3 ++- Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs index ea1ff3546c..2ed8604f36 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyFactory.cs @@ -82,7 +82,8 @@ public static Key Materialize(Domain domain, string nodeId, TypeInfo type, TypeReferenceAccuracy accuracy, params object[] values) { var keyInfo = type.Key; - ArgumentValidator.EnsureArgumentIsInRange(values.Length, 1, keyInfo.TupleDescriptor.Count, "values"); + ArgumentOutOfRangeException.ThrowIfLessThan(values.Length, 1, "values.Length"); + ArgumentOutOfRangeException.ThrowIfGreaterThan(values.Length, keyInfo.TupleDescriptor.Count, "values.Length"); var tuple = Tuple.Create(keyInfo.TupleDescriptor); int typeIdIndex = keyInfo.TypeIdColumnIndex; diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 28659f8a1d..5ce635cc2b 100644 --- a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs +++ b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs @@ -77,7 +77,8 @@ IEnumerator IEnumerable.GetEnumerator() /// public TupleDescriptor Head(int fieldCount) { - ArgumentValidator.EnsureArgumentIsInRange(fieldCount, 1, Count, nameof(fieldCount)); + ArgumentOutOfRangeException.ThrowIfLessThan(fieldCount, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(fieldCount, this.fieldTypes.Length); var fieldTypes = new Type[fieldCount]; Array.Copy(this.fieldTypes, 0, fieldTypes, 0, fieldCount); return new TupleDescriptor(fieldTypes); @@ -92,7 +93,8 @@ public TupleDescriptor Head(int fieldCount) /// public TupleDescriptor Tail(int tailFieldCount) { - ArgumentValidator.EnsureArgumentIsInRange(tailFieldCount, 1, Count, nameof(tailFieldCount)); + ArgumentOutOfRangeException.ThrowIfLessThan(tailFieldCount, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(tailFieldCount, this.fieldTypes.Length); var fieldTypes = new Type[tailFieldCount]; Array.Copy(this.fieldTypes, Count - tailFieldCount, fieldTypes, 0, tailFieldCount); return new TupleDescriptor(fieldTypes); From d8d8f299fbf79b1ea301b365baca6202e594eec4 Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Thu, 16 Apr 2026 19:42:57 +0500 Subject: [PATCH 63/64] Additional tests of TupleDescriptor --- .../Tuples/TupleDescriptorTest.cs | 139 +++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/TupleDescriptorTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/TupleDescriptorTest.cs index dd14dd9584..47c52b8547 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/TupleDescriptorTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/TupleDescriptorTest.cs @@ -14,7 +14,7 @@ namespace Xtensive.Orm.Tests.Core.Tuples [TestFixture] public class TupleDescriptorTest { - public static readonly Type[] FieldTypes = new Type[] { + public readonly Type[] FieldTypes = new Type[] { typeof (bool), typeof (bool?), typeof (byte), @@ -83,7 +83,142 @@ public void PerformanceTest() descriptors.Add(TupleDescriptor.Create(types.ToArray())); } } - + + [Test] + [Explicit] + [Category("Performance")] + public void PerformanceOfConcatTest() + { + var rnd = RandomManager.CreateRandom(SeedVariatorType.CallingMethod); + var count = 100000; + var types = new List(); + + var size = 1; + var sizeBig = 25; + for (var i = 0; i < size; i++) + types.Add(FieldTypes[rnd.Next(FieldTypes.Length)]); + var firstTiny = TupleDescriptor.Create(types.ToArray()); + var secondTiny = TupleDescriptor.Create(types.ToArray()); + + types = new List(25); + for (var i = 0; i < sizeBig; i++) + types.Add(FieldTypes[rnd.Next(FieldTypes.Length)]); + var firstBig = TupleDescriptor.Create(types.ToArray()); + var secondBig = TupleDescriptor.Create(types.ToArray()); + + _ = firstTiny.ConcatWith(secondTiny); + _ = firstBig.ConcatWith(secondBig); + + for (var runIdx = 0; runIdx < 10; runIdx++) { + TestHelper.CollectGarbage(); + + using (var mx = new Measurement("Concating descriptors", count)) { + for (var i = 0; i < count; i++) { + _ = firstTiny.ConcatWith(secondTiny); + } + mx.Complete(); + Console.WriteLine(mx.ToString()); + } + } + + Console.WriteLine(); + for (var runIdx = 0; runIdx < 10; runIdx++) { + TestHelper.CollectGarbage(); + + using (var mx = new Measurement("Concating descriptors", count)) { + for (var i = 0; i < count; i++) { + _ = firstBig.ConcatWith(secondBig); + } + mx.Complete(); + Console.WriteLine(mx.ToString()); + } + } + } + + [Test] + public void GetHeadingPartTest() + { + var dAll = TupleDescriptor.Create(FieldTypes); + _ = Assert.Throws(() => dAll.Head(-1)); + var head1 = dAll.Head(1); + Assert.That(head1.Count, Is.EqualTo(1)); + Assert.That(head1[0], Is.EqualTo(dAll[0])); + var head5 = dAll.Head(5); + Assert.That(head5.Count, Is.EqualTo(5)); + Assert.That(head5[0], Is.EqualTo(dAll[0])); + Assert.That(head5[1], Is.EqualTo(dAll[1])); + Assert.That(head5[2], Is.EqualTo(dAll[2])); + Assert.That(head5[3], Is.EqualTo(dAll[3])); + Assert.That(head5[4], Is.EqualTo(dAll[4])); + + _ = Assert.Throws(() => dAll.Head(FieldTypes.Length + 1)); + } + + [Test] + public void GetTailPartTest() + { + var dAll = TupleDescriptor.Create(FieldTypes); + _ = Assert.Throws(() => dAll.Tail(0)); + var tail1 = dAll.Tail(1); + Assert.That(tail1.Count, Is.EqualTo(1)); + Assert.That(tail1[0], Is.EqualTo(dAll[^1])); + var tail5 = dAll.Tail(5); + Assert.That(tail5.Count, Is.EqualTo(5)); + Assert.That(tail5[0], Is.EqualTo(dAll[^5])); + Assert.That(tail5[1], Is.EqualTo(dAll[^4])); + Assert.That(tail5[2], Is.EqualTo(dAll[^3])); + Assert.That(tail5[3], Is.EqualTo(dAll[^2])); + Assert.That(tail5[4], Is.EqualTo(dAll[^1])); + + _ = Assert.Throws(() => dAll.Tail(FieldTypes.Length + 1)); + } + + [Test] + public void GetSegmentTest() + { + var dAll = TupleDescriptor.Create(FieldTypes); + _ = Assert.Throws(() => dAll.Segment(new Xtensive.Core.Segment(-1, 2))); + + var head2 = dAll.Segment(new Xtensive.Core.Segment(0, 2)); + Assert.That(head2.Count, Is.EqualTo(2)); + Assert.That(head2[0], Is.EqualTo(dAll[0])); + Assert.That(head2[1], Is.EqualTo(dAll[1])); + var middle5 = dAll.Segment(new Xtensive.Core.Segment(5, 5)); + Assert.That(middle5.Count, Is.EqualTo(5)); + Assert.That(middle5[0], Is.EqualTo(dAll[5])); + Assert.That(middle5[1], Is.EqualTo(dAll[6])); + Assert.That(middle5[2], Is.EqualTo(dAll[7])); + Assert.That(middle5[3], Is.EqualTo(dAll[8])); + Assert.That(middle5[4], Is.EqualTo(dAll[9])); + var complete = dAll.Segment(new Xtensive.Core.Segment(0, dAll.Count)); + Assert.That(complete, Is.EqualTo(dAll)); + + _ = Assert.Throws(() => dAll.Segment(new Xtensive.Core.Segment(0, FieldTypes.Length + 2))); + _ = Assert.Throws(() => dAll.Segment(new Xtensive.Core.Segment(1, FieldTypes.Length + 3))); + _ = Assert.Throws(() => dAll.Segment(new Xtensive.Core.Segment(2, FieldTypes.Length + 4))); + } + + [Test] + public void ConcatDescriptorsTest() + { + var d1 = TupleDescriptor.Create(new Type[] { typeof(bool), typeof(int?), typeof(string) }); + var d2 = TupleDescriptor.Create(new Type[] { typeof(bool?), typeof(int?) }); + Assert.That(d1, Is.Not.EqualTo(default(TupleDescriptor))); + Assert.That(d2, Is.Not.EqualTo(default(TupleDescriptor))); + + var concated = d1.ConcatWith(d2); + Assert.That(concated, Is.Not.EqualTo(default(TupleDescriptor))); + Assert.That(concated.Count, Is.EqualTo(d1.Count + d2.Count)); + + for (int firstPart = 0; firstPart < d1.Count; firstPart++) { + Assert.That(concated[firstPart], Is.EqualTo(d1[firstPart])); + } + + for (int secondPart = d1.Count, origIndex = 0; secondPart < d2.Count; secondPart++, origIndex++) { + Assert.That(concated[secondPart], Is.EqualTo(d2[origIndex])); + } + } + [Test] public void CombinedTest() { From f3f1763e495cec7e0c343025d1d954c395b14d4b Mon Sep 17 00:00:00 2001 From: Alexey Kulakov Date: Fri, 17 Apr 2026 17:23:10 +0500 Subject: [PATCH 64/64] Improve changelog --- ChangeLog/7.3.0-Beta-1-dev.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog/7.3.0-Beta-1-dev.txt b/ChangeLog/7.3.0-Beta-1-dev.txt index 20dfbae5ab..7c0a4336d2 100644 --- a/ChangeLog/7.3.0-Beta-1-dev.txt +++ b/ChangeLog/7.3.0-Beta-1-dev.txt @@ -2,6 +2,10 @@ [main] Removed obsolete member Values of SqlInsert type [main] Removed obsolete methods GetSingleConstructor/GetSingleConstructorOrDefault of TypeHelper type [main] Removed obsolete constant WellKnown.MaxNumberOfConditions, use DefaultMaxNumberOfConditions instead +[main] Tuple transformation heavily refactored, many types from Xtensive.Tuples.Transform removed, renamed or turned to internal +[main] Collection FixedList3 is removed +[main] Xtensive.Orm.Rse.Providers.Provider descendants no longer build RecordSetHeader on initialization via virtual method +[main] Many compilable providers build headers more easily with static methods [firebird] Switch to .NET8/.NET10-only support, nuget packages actualized [firebird] Support for Firebird 2.5 ended due to end-of-life state [mysql] Switch to .NET8/.NET10-only support, nuget packages actualized