diff --git a/ChangeLog/7.2.2-dev.txt b/ChangeLog/7.2.2-dev.txt deleted file mode 100644 index ef390785c4..0000000000 --- a/ChangeLog/7.2.2-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -[main] Query.CreateDelayedQuery(key, Func>) applies external key instead of default computed, as it suppose to -[main] QueryEndpoint.SingleAsync()/SingleOrDefaultAsync() get overloads that can recieve one key value as parameter without need to create array explicitly -[main] PrefetchQuery implements IAsyncEnumerable, extra call for .AsAsyncEmumerable() is not needed for async enumeration -[main] PrefetchQuery.AsAsyncEmumerable() is marked as Obsolete -[main] Support for C#14+ optimization that applies ReadOnlySpan.Contains() extension instead of IEnumerable.Contains() one to arrays -[main] Compatibility with VB and FSharp improved \ No newline at end of file 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 diff --git a/Directory.Build.props b/Directory.Build.props index 128282c6ee..66475058ba 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -122,7 +122,7 @@ true snupkg true - true + false diff --git a/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs b/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs index aa6f986cac..8bd075f571 100644 --- a/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs +++ b/Extensions/Xtensive.Orm.Security/Cryptography/MD5HashingService.cs @@ -5,7 +5,6 @@ // Created: 2011.05.22 using System.Security.Cryptography; -using System.Text; using Xtensive.IoC; namespace Xtensive.Orm.Security.Cryptography diff --git a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Resources/Strings.Designer.cs b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Resources/Strings.Designer.cs index 0837199e6b..53424113af 100644 --- a/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Resources/Strings.Designer.cs +++ b/Orm/Xtensive.Orm.Firebird/Sql.Drivers.Firebird/Resources/Strings.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 diff --git a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs index d310637564..87610ffd17 100644 --- a/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs +++ b/Orm/Xtensive.Orm.Sqlite/Sql.Drivers.Sqlite/ProviderInitializer.cs @@ -54,12 +54,11 @@ private static Stream GetLibraryStream() private static string GetLibraryHash() { using (var hashProvider = SHA1.Create()) { - //hashProvider.Initialize(); - ReadOnlySpan hashRaw; - using (var stream = GetLibraryStream()) { - hashRaw = hashProvider.ComputeHash(stream); - } - return new StringBuilder().AppendHexArray(hashRaw[..8]).ToString(); + hashProvider.Initialize(); + using (var stream = GetLibraryStream()) + hashProvider.ComputeHash(stream); + var hash = hashProvider.Hash.Take(8).ToArray(); + return new StringBuilder().AppendHexArray(hash).ToString(); } } 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 500c5115d5..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: {t1}, {t2}, index {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: {wt1}"); - Xtensive.Tuples.Tuple ct1 = ct.Apply(TupleTransformType.Tuple, t1, t2); - TestLog.Info($"Copy: {ct1}"); - Xtensive.Tuples.Tuple wt2 = ctro.Apply(TupleTransformType.TransformedTuple, t1, t2); - Xtensive.Tuples.Tuple ct2 = ctro.Apply(TupleTransformType.Tuple, t1, t2); - Assert.That(wt2, Is.EqualTo(wt1)); - Assert.That(ct1, Is.EqualTo(wt2)); - Assert.That(ct2, Is.EqualTo(ct1)); - - TestLog.InfoRegion("CutInTransform test"); - TestLog.Info($"Originals: {t1}, {value}, index {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: {wtt1}"); - Xtensive.Tuples.Tuple ctt1 = ctt.Apply(TupleTransformType.Tuple, t1, value); - TestLog.Info($"Copy: {ctt1}"); - Xtensive.Tuples.Tuple wtt2 = cttro.Apply(TupleTransformType.TransformedTuple, t1, value); - Xtensive.Tuples.Tuple ctt2 = cttro.Apply(TupleTransformType.Tuple, t1, value); - Assert.That(wtt2, Is.EqualTo(wtt1)); - Assert.That(ctt1, Is.EqualTo(wtt2)); - Assert.That(ctt2, Is.EqualTo(ctt1)); - - } - - [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.Tests.Core/Tuples/Transform/MapTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MapTransformTest.cs index 9cc8e70fe2..4654d238e4 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 MapTransform(true, TupleDescriptor.Create(), new[] {-1, 0}); + var result = transform.Apply(TupleTransformType.TransformedTuple, source); Assert.That(result.GetFieldState(0), Is.EqualTo(TupleFieldState.Default)); Assert.That(result.GetFieldState(1), Is.EqualTo(TupleFieldState.Available)); Assert.That(result.GetFieldState(2), Is.EqualTo(TupleFieldState.Default)); diff --git a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs index bb1b370ff0..f7a11e5168 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Tuples/Transform/MergeTransformTest.cs @@ -1,24 +1,21 @@ -// 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 { [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() @@ -27,8 +24,8 @@ public void BaseTest() Xtensive.Tuples.Tuple t2 = Xtensive.Tuples.Tuple.Create(3, 4.0, "5"); TestLog.Info($"Originals: {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: {wt1}"); @@ -57,34 +54,27 @@ public void BaseTest() AssertEx.Throws(delegate { wtro.SetValue(2, 0); }); + } + + [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)")); - 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: {wt3}"); - Xtensive.Tuples.Tuple ct3 = mt3.Apply(TupleTransformType.Tuple, t1, t1, t1); - TestLog.Info($"Copy: {ct3}"); - t1.SetValue(0,0); - Assert.That(t1.GetValue(0), Is.EqualTo(wt3.GetValue(4))); - 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: {wt4}"); - Xtensive.Tuples.Tuple ct4 = mt4.Apply(TupleTransformType.Tuple, t1, t1, t1, t1); - TestLog.Info($"Copy: {ct4}"); - t1.SetValue(0,0); - Assert.That(t1.GetValue(0), Is.EqualTo(wt4.GetValue(6))); - t1.SetValue(0,1); } [Test] [Explicit] [Category("Performance")] - public void PerformanceTest1() + public void ComparisonPerformanceTest() { 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); @@ -96,55 +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); - 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(delegate { - rt.SetValue(0, 2); - }); - - Xtensive.Tuples.Tuple ct = t.ToReadOnly(TupleTransformType.Tuple); - TestLog.Info($"Copy: {ct}"); - Assert.That(ct, Is.EqualTo(t)); - t.SetValue(0, 2); - Assert.That(ct, Is.Not.EqualTo(t)); - 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 comparer = AdvancedComparerStruct.Default; Xtensive.Tuples.Tuple t = Xtensive.Tuples.Tuple.Create(1, 2, 3, 4); @@ -72,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 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() { diff --git a/Orm/Xtensive.Orm.Tests.Framework/Orm.config b/Orm/Xtensive.Orm.Tests.Framework/Orm.config index acd57272e9..01d89b3eb2 100644 --- a/Orm/Xtensive.Orm.Tests.Framework/Orm.config +++ b/Orm/Xtensive.Orm.Tests.Framework/Orm.config @@ -124,7 +124,7 @@ + connectionString="User ID=dotest;Password=dotest;Data Source=localhost;Initial Catalog=DO-Tests;MultipleActiveResultSets=True;Encrypt=False" /> 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 diff --git a/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs b/Orm/Xtensive.Orm.Tests/Upgrade/ConflictsByTable/TestBase.cs index f5744c280e..dae91d612e 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 { 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/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index c8c6cc54d4..ab46981b3a 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 72bd04e79a..6f52fa050d 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; @@ -35,6 +34,28 @@ public abstract class EntitySetBase : SessionBound, INotifyPropertyChanged, INotifyCollectionChanged { + #region Nested types + 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; + } + } + #endregion + private static readonly string presentationFrameworkAssemblyPrefix = "PresentationFramework,"; #if DEBUG private static readonly string storageTestsAssemblyPrefix = "Xtensive.Orm.Tests"; @@ -45,7 +66,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; @@ -903,8 +924,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(); } @@ -929,30 +949,30 @@ 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; var targetDescriptor = association.TargetType.Key.TupleDescriptor; + var ownerFieldCount = ownerDescriptor.Count; - var itemColumnOffsets = association.AuxiliaryType == null - ? association.UnderlyingIndex.ValueColumns + 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); - - var map = Enumerable.Range(0, ownerDescriptor.Count) - .Select(i => new Pair(0, i)) - .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) - .ToArray(keyFieldCount); - var seekTransform = new MapTransform(true, keyDescriptor, map); + .Select(ci => ci.Field.MappingInfo.Offset)).ToList(); + } + else { + var keyFieldCount = ownerDescriptor.Count + targetDescriptor.Count; + var offsetMap = new int[keyFieldCount]; + for (var index = 0; index < keyFieldCount; index++) { + offsetMap[index] = index - ownerFieldCount; + } + itemColumnOffsets = offsetMap; + + } + var keyDescriptor = ownerDescriptor.ConcatWith(targetDescriptor); Func itemCtor = null; if (association.AuxiliaryType != null) { @@ -961,7 +981,8 @@ 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 int? GetItemIndex(EntitySetState state, Key key) @@ -1029,12 +1050,11 @@ 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]; - auxilaryTypeKeyTransform = new CombineTransform( - false, + auxilaryTypeKeyTransform = new ConcatTransform( owner.TypeInfo.Key.TupleDescriptor, itemType.Key.TupleDescriptor); } diff --git a/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs b/Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs index 29c75b99f9..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 { @@ -19,17 +15,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; } 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/Orm/Internals/KeyRemapper.cs b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs index 615f42d08a..78a19d60a8 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/KeyRemapper.cs @@ -1,10 +1,9 @@ -// 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 // Created: 2014.05.06 -using System; using System.Linq; using Xtensive.Tuples.Transform; @@ -54,7 +53,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(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/Internals/Prefetch/EntitySetTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs index 618f3a6b1e..c016df9e00 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 344cb5d8b8..2c1a19e244 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs @@ -142,7 +142,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 0802be3b7e..15b3a8cffe 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; @@ -67,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++) { @@ -119,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) { this.ownerKey = ownerKey ?? throw new ArgumentNullException(nameof(ownerKey)); this.referencingFieldDescriptor = referencingFieldDescriptor ?? throw new ArgumentNullException(nameof(referencingFieldDescriptor)); 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; diff --git a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs index 9848fbaec1..8f79b5a8fc 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs @@ -14,8 +14,8 @@ internal readonly struct TypeMapping { public readonly TypeInfo Type; public readonly MapTransform KeyTransform; - public readonly IReadOnlyList KeyIndexes; public readonly MapTransform Transform; + public readonly IReadOnlyList KeyIndexes; // Constructors diff --git a/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs b/Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs index 22ef60d79e..5d2a44ac72 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; @@ -93,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/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/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/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index a0144281ec..9940dbae41 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; @@ -702,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/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index e98d4150e2..035c9b3885 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -58,7 +58,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; @@ -546,9 +545,10 @@ public IDictionary, FieldInfo> StructureFieldMapping /// 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; } @@ -563,7 +563,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; } public IEnumerable GetTargetAssociations() @@ -877,13 +881,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; } diff --git a/Orm/Xtensive.Orm/Orm/Operations/EntitySetOperation.cs b/Orm/Xtensive.Orm/Orm/Operations/EntitySetOperation.cs index 0cebe22e46..c17dc0c681 100644 --- a/Orm/Xtensive.Orm/Orm/Operations/EntitySetOperation.cs +++ b/Orm/Xtensive.Orm/Orm/Operations/EntitySetOperation.cs @@ -56,7 +56,7 @@ protected EntitySetOperation(Key key, FieldInfo field) throw new ArgumentOutOfRangeException( Strings.ExTypeOfXMustBeADescendantOfYType, fieldName, - WellKnownOrmTypes.EntitySetBase.GetShortName()); + WellKnownOrmTypes.EntitySetBase.Name); } /// diff --git a/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs b/Orm/Xtensive.Orm/Orm/Operations/KeyGenerateOperation.cs index 1c68689bab..35f0507925 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.Include.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs index 3f38981f07..c313f6e6aa 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]) .ToArray(provider.FilteredColumns.Count); 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 d6f08c7329..9f5b8f8c4a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.cs @@ -526,7 +526,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/Providers/SqlIncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs index 2cd816488a..de7aeb7be9 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs @@ -123,7 +123,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 227990d31b..a26b66cbf0 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,25 +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 (permanentReference is null) - permanentReference = SqlDml.QueryRef(Request.Statement); - return permanentReference; - } - } + public SqlTable PermanentReference => permanentReference ??= SqlDml.QueryRef(Request.Statement); /// /// Gets the domain handler this provider is bound to. @@ -92,23 +83,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(provider.Sources.Count)) - { - 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 a71278390b..28685cf7cb 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlStoreProvider.cs @@ -61,7 +61,6 @@ public SqlStoreProvider( StoreProvider origin, ExecutableProvider source) : base(handlers, request, descriptor, origin, new[] {source}) { - Initialize(); } } } diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlTemporaryDataProvider.cs index c3415d52c5..70b5785095 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; using System.Threading.Tasks; @@ -27,7 +26,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/QueryableExtensions.cs b/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs index 36381597b6..03e5b34906 100644 --- a/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs +++ b/Orm/Xtensive.Orm/Orm/QueryableExtensions.cs @@ -89,8 +89,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. @@ -137,8 +137,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. @@ -161,8 +161,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. diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs index 297e7e0525..cc69851625 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/AggregateProvider.cs @@ -6,14 +6,9 @@ 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,25 +25,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; } - - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header - .Select(GroupColumnIndexes) - .Add(AggregateColumns); - } + public int[] GroupColumnIndexes { get; } /// protected override string ParametersToString() @@ -69,21 +51,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. /// @@ -188,6 +155,48 @@ private static NotSupportedException AggregateNotSupported(Type sourceColumnType #endregion + #region Header build + private static RecordSetHeader BuildHeaderAndColumns( + CompilableProvider source, + IReadOnlyList columnDescriptors, + ref int[] groupIndexes, + out AggregateColumn[] aggregateColumns) + { + groupIndexes ??= Array.Empty(); + var descriptorsCount = columnDescriptors.Count; + aggregateColumns = new AggregateColumn[descriptorsCount]; + var sourceHeader = source.Header; + var sourceHeaderColumns = sourceHeader.Columns; + for (int i = 0; i < descriptorsCount; i++) { + var agrColumnDescriptor = columnDescriptors[i]; + var type = GetAggregateColumnType(sourceHeaderColumns[agrColumnDescriptor.SourceIndex].Type, agrColumnDescriptor.AggregateType); + aggregateColumns[i] = new AggregateColumn(agrColumnDescriptor, groupIndexes.Length + i, type); + } + + return sourceHeader.Select(groupIndexes).Add(aggregateColumns); + } + + private static RecordSetHeader BuildHeaderAndColumns( + CompilableProvider source, + IReadOnlyList columns, + ref int[] groupIndexes, + out AggregateColumn[] aggregateColumns) + { + groupIndexes ??= Array.Empty(); + var descriptorsCount = columns.Count; + aggregateColumns = new AggregateColumn[descriptorsCount]; + var sourceHeader = source.Header; + var sourceHeaderColumns = sourceHeader.Columns; + for (int i = 0; i < descriptorsCount; i++) { + var agrColumnDescriptor = columns[i].Descriptor; + var type = GetAggregateColumnType(sourceHeaderColumns[agrColumnDescriptor.SourceIndex].Type, agrColumnDescriptor.AggregateType); + aggregateColumns[i] = new AggregateColumn(agrColumnDescriptor, groupIndexes.Length + i, type); + } + + return sourceHeader.Select(groupIndexes).Add(aggregateColumns); + } + #endregion + // Constructors /// @@ -197,19 +206,10 @@ private static NotSupportedException AggregateNotSupported(Type sourceColumnType /// The column indexes to group by. /// The descriptors of . public AggregateProvider(CompilableProvider source, int[] groupIndexes, IReadOnlyList columnDescriptors) - : base(ProviderType.Aggregate, source) + : base(ProviderType.Aggregate, BuildHeaderAndColumns(source, columnDescriptors, ref groupIndexes, out var columns), source) { - ArgumentNullException.ThrowIfNull(columnDescriptors); - groupIndexes = groupIndexes ?? Array.Empty(); - var columns = new AggregateColumn[columnDescriptors.Count]; - for (int i = 0, count = columnDescriptors.Count; i < count; i++) { - var 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(); } /// @@ -219,21 +219,12 @@ public AggregateProvider(CompilableProvider source, int[] groupIndexes, IReadOnl /// The column indexes to group by. /// Columns of old AggregateProvider as source of descriptors. internal AggregateProvider(CompilableProvider source, int[] groupIndexes, IReadOnlyList descriptorSource) - : base(ProviderType.Aggregate, source) + : base(ProviderType.Aggregate, BuildHeaderAndColumns(source, descriptorSource, ref groupIndexes, out var columns), source) { // Having this dedicated ctor saves some resources on not having to make // an array just to pass descriptors for simple enumeration - groupIndexes = groupIndexes ?? Array.Empty(); - var columns = new AggregateColumn[descriptorSource.Count]; - for (int i = 0, count = descriptorSource.Count; i < count; i++) { - var sourceDescriptor = descriptorSource[i].Descriptor; - var descriptor = new AggregateColumnDescriptor(sourceDescriptor.Name, sourceDescriptor.SourceIndex, sourceDescriptor.AggregateType); - 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 437b30effe..93519aee97 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,11 +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) { - ArgumentValidator.EnsureArgumentNotNullOrEmpty(alias, nameof(alias)); 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 8c60a47c8c..1e0c675bbe 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 ?? throw new ArgumentNullException(nameof(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 0060229377..8d0211ccb8 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,24 @@ 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) { - Left = left ?? throw new ArgumentNullException(nameof(left)); - Right = right ?? throw new ArgumentNullException(nameof(right)); + Left = left; + Right = right; } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs index b5a23d7470..a7142712ba 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/CalculateProvider.cs @@ -5,11 +5,8 @@ // Created: 2008.09.09 using System; -using Xtensive.Core; - -using Xtensive.Tuples.Transform; -using Xtensive.Collections; using System.Collections.Generic; +using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers { @@ -23,24 +20,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; } - - - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header.Add(CalculatedColumns); - } + public CalculatedColumn[] CalculatedColumns { get; } /// protected override string ParametersToString() @@ -48,16 +33,23 @@ protected override string ParametersToString() return CalculatedColumns.ToCommaDelimitedString(); } - /// - protected override void Initialize() + #region Header build + private static RecordSetHeader BuildHeaderAndColumns( + CompilableProvider source, + IReadOnlyList columnDescriptors, + out CalculatedColumn[] calculatedColumns) { - 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; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); - } + var sourceHeader = source.Header; + var sourceHeaderLength = sourceHeader.Length; + var descriptorsCount = columnDescriptors.Count; + calculatedColumns = new CalculatedColumn[descriptorsCount]; + for (int i = 0; i < descriptorsCount; i++) { + calculatedColumns[i] = new CalculatedColumn(columnDescriptors[i], sourceHeaderLength + i); + } + return sourceHeader.Add(calculatedColumns); + } + #endregion // Constructors @@ -68,18 +60,10 @@ protected override void Initialize() /// The property value. /// The descriptors of . public CalculateProvider(CompilableProvider source, IReadOnlyList columnDescriptors, bool isInlined = false) - : base(ProviderType.Calculate, source) + : base(ProviderType.Calculate, BuildHeaderAndColumns(source, columnDescriptors, out var calculatedColumns), source) { - ArgumentNullException.ThrowIfNull(columnDescriptors); - IsInlined = isInlined; - var columns = new CalculatedColumn[columnDescriptors.Count]; - for (int i = 0, count = columnDescriptors.Count; i < count; i++) { - var col = new CalculatedColumn(columnDescriptors[i], Source.Header.Length + i); - columns.SetValue(col, 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 57a743908c..3bb954dfe0 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ConcatProvider.cs @@ -5,10 +5,8 @@ // Created: 2009.04.01 using System; +using System.Buffers; using System.Collections.Generic; -using Xtensive.Collections; - - using System.Linq; namespace Xtensive.Orm.Rse.Providers @@ -20,44 +18,55 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ConcatProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() + #region Header build + 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]; + 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)); + } + 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 columnGroups = Left.Header.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); + 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( - 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)) - throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Concatenation")); + var left = leftHeader.TupleDescriptor; + var right = rightHeader.TupleDescriptor; + if (!left.Equals(right)) { + throw new InvalidOperationException(string.Format(Strings.ExXCantBeExecuted, "Concatenation")); + } } - + #endregion // Constructors @@ -67,9 +76,8 @@ private void EnsureConcatIsPossible() /// 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 7f26e926c8..62ff8b20c9 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,65 +18,58 @@ 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() + #region Header build + private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { - return indexHeader; - } + if (fullFeatured) { + var primaryIndexRecordsetHeader = + index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); + var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); + return primaryIndexRecordsetHeader.Add(rankColumn); + } + else { + var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; + if (primaryIndexKeyColumns.Count!=1) + throw new InvalidOperationException(Strings.ExOnlySingleColumnKeySupported); - public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, bool fullFeatured) - : this(index, searchCriteria, rankColumnName, new List(), null, fullFeatured) - { + var keyValueType = primaryIndexKeyColumns[0].Key.ValueType; + var fieldTypes = new Type[2] { keyValueType, WellKnownTypes.Double }; + var tupleDescriptor = TupleDescriptor.Create(fieldTypes); + var columns = new Column[2] { new MappedColumn("KEY", 0, keyValueType), new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double) }; + + 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) - { - + { } public ContainsTableProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, IList targetColumns, Func topNByRank, bool fullFeatured) - : base(ProviderType.ContainsTable) + : base(ProviderType.ContainsTable, BuildHeader(index, rankColumnName, fullFeatured)) { SearchCriteria = searchCriteria ?? throw new ArgumentNullException(nameof(searchCriteria)); FullFeatured = fullFeatured; PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); - TargetColumns = targetColumns.SelectToList(tc => index.Columns.First(c => c.Column == tc)) + TargetColumns = targetColumns.Select(tc => index.Columns.First(c => c.Column == tc)) + .ToList(targetColumns.Count) .AsReadOnly(); TopN = topNByRank; - if (FullFeatured) { - var primaryIndexRecordsetHeader = - index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); - var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); - indexHeader = primaryIndexRecordsetHeader.Add(rankColumn); - } - else { - 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 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);; - indexHeader = new RecordSetHeader(tupleDescriptor, columns); - } - Initialize(); } } } 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..e472702345 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,20 +16,18 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ExceptProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() - { - EnsureIntersectIsPossible(); - return Left.Header; - } - - private void EnsureIntersectIsPossible() + #region Header build + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (left!=right) - throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Except operation")); + 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; } - + #endregion // Constructors @@ -42,9 +37,8 @@ private void EnsureIntersectIsPossible() /// 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 f021d48cd4..c736e9070a 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/ExistenceProvider.cs @@ -5,12 +5,8 @@ // Created: 2009.03.20 using System; -using System.Diagnostics; -using Xtensive.Collections; -using Xtensive.Core; using Xtensive.Reflection; using Xtensive.Tuples; -using Tuple = Xtensive.Tuples.Tuple; namespace Xtensive.Orm.Rse.Providers { @@ -21,19 +17,20 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class ExistenceProvider : UnaryProvider { + private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] { WellKnownTypes.Bool }); + /// /// Gets the name of the existence column. /// - public string ExistenceColumnName { get; private set; } - - private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); + public string ExistenceColumnName { get; } - /// - protected override RecordSetHeader BuildHeader() + #region Header build + private static RecordSetHeader BuildHeader(string existenceColumnName) { return new RecordSetHeader( - BoolTupleDescriptor, new[] { new SystemColumn(ExistenceColumnName, 0, WellKnownTypes.Bool) }); + BoolTupleDescriptor, new[] { new SystemColumn(existenceColumnName, 0, WellKnownTypes.Bool) }); } + #endregion // Constructors @@ -42,11 +39,9 @@ protected override RecordSetHeader BuildHeader() /// Initializes a new instance of this class. /// public ExistenceProvider(CompilableProvider source, string existenceColumnName) - : base(ProviderType.Existence, source) + : base(ProviderType.Existence, BuildHeader(existenceColumnName), source) { - ArgumentValidator.EnsureArgumentNotNullOrEmpty(existenceColumnName, nameof(existenceColumnName)); 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..54fc42e577 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; @@ -21,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() @@ -45,7 +31,6 @@ protected override string ParametersToString() return Predicate.ToString(true); } - // Constructors /// @@ -57,7 +42,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 3d492016c9..d7194090ae 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,20 +20,37 @@ 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() + #region Header build + private static RecordSetHeader BuildHeader(FullTextIndexInfo index, string rankColumnName, bool fullFeatured) { - return indexHeader; + if (fullFeatured) { + var primaryIndexRecordsetHeader = index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); + var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); + return primaryIndexRecordsetHeader.Add(rankColumn); + } + else { + var primaryIndexKeyColumns = index.PrimaryIndex.KeyColumns; + if (primaryIndexKeyColumns.Count!=1) + throw new InvalidOperationException(Strings.ExOnlySingleColumnKeySupported); + + var keyValueType = primaryIndexKeyColumns[0].Key.ValueType; + var fieldTypes = new Type[2] { keyValueType, WellKnownTypes.Double }; + var tupleDescriptor = TupleDescriptor.Create(fieldTypes); + var columns = new Column[2] { new MappedColumn("KEY", 0, keyValueType), new MappedColumn("RANK", tupleDescriptor.Count, WellKnownTypes.Double) }; + return new RecordSetHeader(tupleDescriptor, columns); + } } + #endregion + + // Constructors public FreeTextProvider(FullTextIndexInfo index, Func searchCriteria, string rankColumnName, bool fullFeatured) : this(index, searchCriteria, rankColumnName, null, fullFeatured) @@ -45,33 +59,12 @@ public FreeTextProvider(FullTextIndexInfo index, Func public FreeTextProvider( FullTextIndexInfo index, Func searchCriteria, string rankColumnName, Func topN, bool fullFeatured) - : base(ProviderType.FreeText) + : base(ProviderType.FreeText, BuildHeader(index, rankColumnName, fullFeatured)) { SearchCriteria = searchCriteria ?? throw new ArgumentNullException(nameof(searchCriteria)); FullFeatured = fullFeatured; TopN = topN; PrimaryIndex = new IndexInfoRef(index.PrimaryIndex); - if (FullFeatured) { - var primaryIndexRecordsetHeader = index.PrimaryIndex.ReflectedType.Indexes.PrimaryIndex.GetRecordSetHeader(); - var rankColumn = new MappedColumn(rankColumnName, primaryIndexRecordsetHeader.Length, WellKnownTypes.Double); - indexHeader = primaryIndexRecordsetHeader.Add(rankColumn); - } - else { - 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 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); - indexHeader = new RecordSetHeader(tupleDescriptor, columns); - } - Initialize(); } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs index 6c0dd47899..d053630d96 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/IncludeProvider.cs @@ -9,12 +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.Tuples.Transform; -using System.Linq; -using Xtensive.Reflection; namespace Xtensive.Orm.Rse.Providers { @@ -30,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. @@ -51,28 +48,24 @@ public sealed class IncludeProvider: UnaryProvider, /// /// Gets filter data. /// - public Expression>> FilterDataSource { get; private set; } - - public MapTransform FilteredColumnsExtractionTransform { get; private set; } - - public CombineTransform ResultTransform { get; private set; } + public Expression>> FilterDataSource { get; } - private static readonly TupleDescriptor BoolTupleDescriptor = TupleDescriptor.Create(new[] {WellKnownTypes.Bool}); + public TupleDescriptor FilteredTupleDescriptor { get; } - /// - protected override RecordSetHeader BuildHeader() + #region Header build + private static RecordSetHeader BuildHeaderAndFilteredTupleDescriptor( + CompilableProvider source, IReadOnlyList filteredColumns, string resultColumnName, out TupleDescriptor filteredTupleDescriptor) { - var newHeader = Source.Header.Add(new SystemColumn(ResultColumnName, Source.Header.Length, WellKnownTypes.Bool)); - var fieldTypes = new Type[FilteredColumns.Count]; - for (var index = 0; index < fieldTypes.Length; index++) { - fieldTypes[index] = newHeader.Columns[FilteredColumns[index]].Type; + 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++) { + fieldTypes[index] = header.Columns[filteredColumns[index]].Type; } - var tupleDescriptor = TupleDescriptor.Create(fieldTypes); - FilteredColumnsExtractionTransform = new MapTransform(true, tupleDescriptor, FilteredColumns); - ResultTransform = new CombineTransform(true, Source.Header.TupleDescriptor, BoolTupleDescriptor); - return newHeader; + filteredTupleDescriptor = TupleDescriptor.Create(fieldTypes); + return header; } - + #endregion // Constructors @@ -87,29 +80,15 @@ protected override RecordSetHeader BuildHeader() /// 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.EnsureArgumentNotNullOrEmpty(resultColumnName, nameof (resultColumnName)); - ArgumentNullException.ThrowIfNull(filteredColumns); - Algorithm = algorithm; IsInlined = isInlined; FilterDataSource = filterDataSource ?? throw new ArgumentNullException(nameof(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..de2a4f7a3b 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,21 +16,19 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class IntersectProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() - { - EnsureIntersectIsPossible(); - return Left.Header; - } - - private void EnsureIntersectIsPossible() + #region Header build + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - var left = Left.Header.TupleDescriptor; - var right = Right.Header.TupleDescriptor; - if (left!=right) - throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Intersection")); + 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; } + #endregion - // Constructors /// @@ -42,9 +37,8 @@ private void EnsureIntersectIsPossible() /// 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..40fceb17e1 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,19 +43,19 @@ protected override string ParametersToString() EqualColumns.Select(p => p.First.Name + " == " + p.Second.Name).ToCommaDelimitedString()); } - /// - protected override void Initialize() + + 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 /// @@ -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 f24546ba2e..dbc5d0c807 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/LockProvider.cs @@ -35,11 +35,8 @@ public sealed class LockProvider : UnaryProvider /// 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) + : this(source, () => lockMode, () => lockBehavior) { - LockMode = () => lockMode; - LockBehavior = () => lockBehavior; - Initialize(); } /// @@ -52,8 +49,7 @@ public LockProvider(CompilableProvider source, Func lockMode, Func /// Sort order of the index. /// - 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() - { - return Source.Header.Sort(Order); - } + public DirectionCollection Order { get; } /// protected override string ParametersToString() @@ -59,32 +30,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 column = Header.Columns[orderItem.Key]; - - var culture = column is MappedColumn mColumn && mColumn.ColumnInfoRef.TypeName != null - ? mColumn.ColumnInfoRef.CultureInfo - : CultureInfo.InvariantCulture; - 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 @@ -95,7 +40,7 @@ protected override void Initialize() /// 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 ?? throw new ArgumentNullException(nameof(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 bb6b520a80..6bfc829934 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 ?? throw new ArgumentNullException(nameof(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 cdabb9db22..4d3b63d090 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,30 +20,17 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class RawProvider : CompilableProvider { - private readonly RecordSetHeader header; private Func> compiledSource; /// /// 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; - } - } - - /// - protected override RecordSetHeader BuildHeader() - { - return header; - } + public Func> CompiledSource => compiledSource ??= Source.CachingCompile(); /// protected override string ParametersToString() @@ -63,11 +47,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 ?? throw new ArgumentNullException(nameof(source)); - this.header = header ?? throw new ArgumentNullException(nameof(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 257440ae8e..03e4604754 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 { @@ -20,29 +18,16 @@ public sealed class RowNumberProvider : UnaryProvider /// /// Gets the row number column. /// - public SystemColumn SystemColumn { get; private set; } + public SystemColumn SystemColumn { get; } - /// - /// Gets header resize transform. - /// - public MapTransform ResizeTransform { get; private set; } - - /// - protected override void Initialize() + #region Header build + private static RecordSetHeader CreateHeaderAndColumn(CompilableProvider source, string columnName, out SystemColumn systemColumn) { - 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; - ResizeTransform = new MapTransform(false, Header.TupleDescriptor, columnIndexes); + var sourceHeader = source.Header; + systemColumn = new SystemColumn(columnName, sourceHeader.Length, WellKnownTypes.Int64); + return sourceHeader.Add(systemColumn); } - - /// - protected override RecordSetHeader BuildHeader() - { - return Source.Header.Add(SystemColumn); - } - + #endregion // Constructors @@ -52,10 +37,9 @@ protected override RecordSetHeader BuildHeader() /// 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..c93ba61dd6 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 @@ -23,7 +19,7 @@ public sealed class SeekProvider : UnaryProvider /// /// Seek parameter. /// - public Func Key { get; private set; } + public Func Key { get; } /// protected override string ParametersToString() @@ -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 5b094fa504..239b465b17 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,23 +36,9 @@ 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) { - ArgumentNullException.ThrowIfNull(columnIndexes); - - switch (columnIndexes) { - case int[] indexArray: - ColumnIndexes = Array.AsReadOnly(indexArray); - break; - case List indexList: - ColumnIndexes = indexList.AsReadOnly(); - break; - default: - ColumnIndexes = columnIndexes; - break; - } - - Initialize(); + ColumnIndexes = columnIndexes; } } } \ 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 819ad69d1e..052506f103 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/StoreProvider.cs @@ -16,8 +16,6 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class StoreProvider : CompilableProvider { - private readonly RecordSetHeader header; - /// /// Gets the name of saved data. /// @@ -28,12 +26,6 @@ public sealed class StoreProvider : CompilableProvider /// public CompilableProvider Source { get; } - /// - protected override RecordSetHeader BuildHeader() - { - return header; - } - /// protected override string ParametersToString() { @@ -43,39 +35,18 @@ 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) - { - ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, "name"); - Name = name; - - this.header = header ?? throw new ArgumentNullException(nameof(header)); - - Initialize(); - } - /// /// Initializes a new instance of this class. /// /// The property value. /// The property value. public StoreProvider(CompilableProvider source, string name) - : base(ProviderType.Store, source) + : base(ProviderType.Store, source.Header, source) { - ArgumentNullException.ThrowIfNull(source); ArgumentValidator.EnsureArgumentNotNullOrEmpty(name, nameof(name)); Name = name; - Source = source; - - header = source.Header; - - Initialize(); + Source = source ?? throw new ArgumentNullException(nameof(source)); } /// @@ -83,14 +54,8 @@ public StoreProvider(CompilableProvider source, string name) /// /// The property value. public StoreProvider(CompilableProvider source) - : base(ProviderType.Store, source) + : this(source, Guid.NewGuid().ToString()) { - Name = Guid.NewGuid().ToString(); - Source = source ?? throw new ArgumentNullException(nameof(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 27178dd2bc..5e443e5b75 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/TagProvider.cs @@ -25,7 +25,6 @@ public TagProvider(CompilableProvider source, string 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 d54b2c87ab..92a4db0460 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnaryProvider.cs @@ -5,8 +5,6 @@ // Created: 2008.07.22 using System; -using Xtensive.Collections; -using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers @@ -19,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 @@ -35,7 +28,18 @@ 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) { Source = source ?? throw new ArgumentNullException(nameof(source)); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs index 9b48469b24..d95a3eb372 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/UnionProvider.cs @@ -5,11 +5,10 @@ // Created: 2009.04.01 using System; +using System.Buffers; using System.Collections.Generic; -using Xtensive.Collections; - - using System.Linq; +using Xtensive.Core; namespace Xtensive.Orm.Rse.Providers { @@ -20,46 +19,55 @@ namespace Xtensive.Orm.Rse.Providers [Serializable] public sealed class UnionProvider : BinaryProvider { - protected override RecordSetHeader BuildHeader() + #region Header build + private static RecordSetHeader BuildHeader(CompilableProvider left, CompilableProvider right) { - EnsureUnionIsPossible(); + 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]; - if (leftColumn is MappedColumn && rightColumn is MappedColumn) { - var leftMappedColumn = (MappedColumn) leftColumn; - var rightMappedColumn = (MappedColumn) rightColumn; + // 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 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 columnGroups = Left.Header.ColumnGroups.Where(cg => cg.Keys.All(mappedColumnIndexes.Contains)).ToList(); + 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( - Left.Header.TupleDescriptor, - columns, + 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)) - throw new InvalidOperationException(String.Format(Strings.ExXCantBeExecuted, "Union operation")); + var left = leftHeader.TupleDescriptor; + var right = rightHeader.TupleDescriptor; + if (!left.Equals(right)) { + throw new InvalidOperationException(string.Format(Strings.ExXCantBeExecuted, "Union operation")); + } } - + #endregion // Constructors @@ -69,9 +77,8 @@ private void EnsureUnionIsPossible() /// 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 59c2d9bd44..e41310d387 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/VoidProvider.cs @@ -4,24 +4,14 @@ // Created by: Denis Krjuchkov // Created: 2012.01.29 -using System; - 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) { - this.header = header ?? throw new ArgumentNullException(nameof(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 5d631a298e..eadf1840f9 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/CompilableProvider.cs @@ -19,13 +19,13 @@ 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) { } - protected CompilableProvider(ProviderType type) - : this(type, Array.Empty()) + protected CompilableProvider(ProviderType type, RecordSetHeader header) + : this(type, header, Array.Empty()) { } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Executable/ExecutableRawProvider.cs index a449e2cde3..3a2964ec5e 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-2021 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 diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs index ceef589d52..c1ba20a441 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/ExecutableProvider.cs @@ -23,10 +23,8 @@ public abstract class ExecutableProvider : Provider /// /// Gets the provider this provider is compiled from. /// - public CompilableProvider Origin { get; private set; } + public CompilableProvider Origin { get; } - /// is . - protected override RecordSetHeader BuildHeader() => Origin.Header; #region OnXxxEnumerate methods (to override) @@ -173,8 +171,8 @@ public async Task GetRecordSetReaderAsync( /// /// The property value. /// The property value. - protected ExecutableProvider(CompilableProvider origin, IReadOnlyList sources) - : base(origin.Type, sources) + protected ExecutableProvider(CompilableProvider origin, ExecutableProvider[] sources) + : base(origin.Type, origin.Header, sources) { Origin = origin; } 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 diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs index abab50d87c..445ed96768 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Provider.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text; using Xtensive.Core; using Xtensive.Reflection; @@ -24,8 +25,6 @@ public abstract class Provider private const string ToString_Parameters = " ({0})"; private const int ToString_IndentSize = 2; - private RecordSetHeader header; - /// /// Gets of the current instance. /// @@ -35,46 +34,14 @@ public abstract class Provider /// Gets or sets the source providers /// "consumed" by this provider to produce results of current provider. /// - public IReadOnlyList Sources { [DebuggerStepThrough] get; } + public Provider[] Sources { get; } /// /// 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; - } - } + public RecordSetHeader Header { get; } - /// - /// Builds the . - /// This method is invoked just once on each provider. - /// - /// Newly created to assign to property. - protected abstract RecordSetHeader BuildHeader(); - - private string DebuggerDisplayName { - get { - var type = GetType(); - return type.IsGenericType - ? type.GetShortName() - : type.Name; - } - } - - /// - /// Performs initialization of the provider. - /// - protected virtual void Initialize() - { - Header = BuildHeader(); - } + private string DebuggerDisplayName => GetType().GetShortName(); #region ToString method @@ -89,14 +56,15 @@ 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) { - sb.Append(TitleToString().Indent(indent)) + _ = sb.Append(TitleToString().Indent(indent)) .AppendLine(); } @@ -105,11 +73,12 @@ private string TitleToString() var sb = new StringBuilder(); var type = GetType(); var providerName = (type.IsGenericType ? type.GetShortName() : type.Name).TryCutSuffix(ToString_ProviderTypeSuffix); - var parameters = ParametersToString(); + 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(); } @@ -118,10 +87,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 @@ -132,11 +98,15 @@ 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, IReadOnlyList sources) + protected Provider(ProviderType type, RecordSetHeader header, Provider[] sources) { Type = type; - Sources = sources; + Header = header ?? throw new ArgumentNullException(nameof(header)); + Sources = (sources.Any(p => p is null)) + ? throw new ArgumentNullException(nameof(header)) + : sources; } } } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs index cb6f58bf5a..58ff02b27a 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 IncludeProvider 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, Enumerable.Repeat(calulatedColumn.Index, 1)); if (source == provider.Source) { return provider; @@ -326,7 +326,7 @@ protected override RowNumberProvider 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, Enumerable.Repeat(rowNumberColumn.Index, 1)); return newSource == provider.Source ? provider diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs index 15f78c81f4..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.ToArray()); + 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/Orm/VersionInfo.cs b/Orm/Xtensive.Orm/Orm/VersionInfo.cs index 89187d8b34..68c0c120ed 100644 --- a/Orm/Xtensive.Orm/Orm/VersionInfo.cs +++ b/Orm/Xtensive.Orm/Orm/VersionInfo.cs @@ -57,7 +57,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) { ArgumentNullException.ThrowIfNull(key); @@ -65,9 +65,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/Orm/VersionValidator.cs b/Orm/Xtensive.Orm/Orm/VersionValidator.cs index e343b8d58e..fcdbd781cf 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; 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/Packed/PackedFieldAccessor.cs b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs index abea03b209..fe8a1e41f5 100644 --- a/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs +++ b/Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs @@ -149,7 +149,7 @@ private static int GetRank(int bitSize) => protected ValueFieldAccessor(int bitCount, byte index) : base(GetRank(bitCount), index) - {} + { } } internal abstract class ValueFieldAccessor : ValueFieldAccessor diff --git a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.cs deleted file mode 100644 index 210fa33d2d..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CombineTransform.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: Alexey Kochetov -// Created: 2008.04.30 - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Xtensive.Core; -using Xtensive.Reflection; - - -namespace Xtensive.Tuples.Transform -{ - /// - /// This class is used for source s combining. - /// - [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); - } - - /// - public override string ToString() - { - string description = $"{sources.ToDelimitedString(" + ")}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, GetType().Name, description); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Source tuple descriptors. - public CombineTransform(bool isReadOnly, params TupleDescriptor[] sources) - : base(isReadOnly) - { - int totalLength = sources.Sum(s => s.Count); - var types = new Type[totalLength]; - var map = new Pair[totalLength]; - int index = 0; - for (int i = 0; i(i, j); - } - } - this.sources = sources; - Descriptor = TupleDescriptor.Create(types); - SetMap(map); - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs new file mode 100644 index 0000000000..3b8974e797 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/ConcatTransform.cs @@ -0,0 +1,115 @@ +// 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 + +using System; +using Xtensive.Core; +using Xtensive.Reflection; +using Xtensive.Tuples.Transform.Internals; + +namespace Xtensive.Tuples.Transform +{ + /// + /// This class is used for concatenation of two s. + /// + [Serializable] + public sealed class ConcatTransform + { + private readonly (int first, int second) sourceParts; + + /// + public TupleDescriptor Descriptor { get; } + + /// + public bool IsReadOnly { get; } + + /// + /// Applies the transformation. + /// + /// The type of transformation to perform. + /// First transformation source. + /// Second transformation source. + /// Transformation result - + /// either instance or the descendant, + /// dependently on specified . + 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() + { + 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); + } + + + // Constructors + + /// + /// Initializes a new instance of this type. + /// + /// 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) + { + 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/CutInTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs deleted file mode 100644 index 222374fe97..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CutInTransform.cs +++ /dev/null @@ -1,110 +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().Name, - 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 34dfd1247e..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/CutOutTransform.cs +++ /dev/null @@ -1,86 +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().Name, 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/ConcatTransformTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs new file mode 100644 index 0000000000..ca29147f87 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/ConcatTransformTuple.cs @@ -0,0 +1,94 @@ +// 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 : Tuple, ITransformedTuple + { + private readonly ConcatTransform transform; + 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(transform.Descriptor); + + /// + public override TupleDescriptor Descriptor => transform.Descriptor; + + #region GetFieldState, GetValue, SetValue methods + + /// + public override TupleFieldState GetFieldState(int fieldIndex) + { + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + 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 == TransformUtil.NoMapping) { + return; + } + source.SetFieldState(index, fieldState); + } + + /// + public override object GetValue(int fieldIndex, out TupleFieldState fieldState) + { + var (source, index) = GetSourceAndFieldIndex(fieldIndex); + return index == TransformUtil.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : source.GetValue(index, out fieldState); + } + + /// + public override void SetValue(int fieldIndex, object fieldValue) + { + if (transform.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, TransformUtil.NoMapping); + } + var source2Index = fieldIndex - source1.Count; + 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) + { + this.transform = transform; + this.source1 = source1; + this.source2 = source2; + totalCount = source1.Count + source2.Count; + } + } +} \ 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 de90a28056..e26be75821 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple.cs @@ -1,73 +1,91 @@ // 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 +// Created by: Alex Yakunin +// Created: 2008.06.04 using System; -using System.Collections.Generic; -using System.Diagnostics; using Xtensive.Core; namespace Xtensive.Tuples.Transform.Internals { /// - /// A result tuple mapping arbitrary count of source tuples to a single one (this). + /// A result tuple mapping 1 source tuple to a single one (this). /// [Serializable] - public sealed class MapTransformTuple : TransformedTuple + internal sealed class MapTransformTuple : Tuple, ITransformedTuple { - private readonly Tuple[] tuples; + private readonly MapTransform transform; + 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(transform.Descriptor); /// - public override IReadOnlyList Arguments - { - [DebuggerStepThrough] - get => Array.AsReadOnly(tuples); - } + public override TupleDescriptor Descriptor => transform.Descriptor; #region GetFieldState, GetValue, SetValue methods /// public override TupleFieldState GetFieldState(int fieldIndex) { - var indexes = TypedTransform.map[fieldIndex]; - return tuples[indexes.First].GetFieldState(indexes.Second); + var index = GetSourceFieldIndex(fieldIndex); + return index == TransformUtil.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); } protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; - tuples[indexes.First].SetFieldState(indexes.Second, fieldState); + var index = GetSourceFieldIndex(fieldIndex); + if (index == TransformUtil.NoMapping) { + return; + } + source.SetFieldState(index, fieldState); } /// public override object GetValue(int fieldIndex, out TupleFieldState fieldState) { - var indexes = TypedTransform.map[fieldIndex]; - return tuples[indexes.First].GetValue(indexes.Second, out fieldState); + int index = GetSourceFieldIndex(fieldIndex); + return index == TransformUtil.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : source.GetValue(index, out fieldState); } /// public override void SetValue(int fieldIndex, object fieldValue) { - if (Transform.IsReadOnly) + if (transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); - var indexes = TypedTransform.map[fieldIndex]; - tuples[indexes.First].SetValue(indexes.Second, fieldValue); + } + source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); } #endregion + private int GetSourceFieldIndex(int fieldIndex) + { + var mappedIndex = transform.Map[fieldIndex]; + return mappedIndex < 0 ? TransformUtil.NoMapping : mappedIndex; + } + protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) { - if (isWriting && Transform.IsReadOnly) + if (isWriting && transform.IsReadOnly) { throw Exceptions.ObjectIsReadOnly(null); - var map = TypedTransform.map[fieldIndex]; - return tuples[map.First].GetMappedContainer(map.Second, isWriting); + } + 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 @@ -75,12 +93,11 @@ protected internal override Pair GetMappedContainer(int fieldIndex, /// Initializes new instance of this type. /// /// The transform. - /// Source tuples. - public MapTransformTuple(MapTransform transform, params Tuple[] sources) - : base(transform) + /// Source tuple. + public MapTransformTuple(MapTransform transform, Tuple source) { - // Other checks are omitted: this transform should be fast, so delayed errors are ok - this.tuples = sources ?? throw new ArgumentNullException(nameof(sources)); + this.transform = transform; + this.source = source ?? throw new ArgumentNullException(nameof(source)); } } } \ 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 deleted file mode 100644 index 7e2ca9dd2b..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple1.cs +++ /dev/null @@ -1,97 +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 System.Collections.Generic; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform.Internals -{ - /// - /// A result tuple mapping 1 source tuple to a single one (this). - /// - [Serializable] - 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 - - /// - public override TupleFieldState GetFieldState(int fieldIndex) - { - int index = GetMappedFieldIndex(fieldIndex); - return index == MapTransform.NoMapping ? TupleFieldState.Default : tuple.GetFieldState(index); - } - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - int index = GetMappedFieldIndex(fieldIndex); - if (index == MapTransform.NoMapping) - return; - tuple.SetFieldState(index, fieldState); - } - - /// - 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); - } - - /// - public override void SetValue(int fieldIndex, object fieldValue) - { - if (Transform.IsReadOnly) - throw Exceptions.ObjectIsReadOnly(null); - tuple.SetValue(GetMappedFieldIndex(fieldIndex), fieldValue); - } - - #endregion - - private int GetMappedFieldIndex(int fieldIndex) - { - int mappedIndex = TypedTransform.singleSourceMap[fieldIndex]; - return mappedIndex < 0 ? MapTransform.NoMapping :mappedIndex; - } - - protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) - { - if (isWriting && Transform.IsReadOnly) - throw Exceptions.ObjectIsReadOnly(null); - var index = GetMappedFieldIndex(fieldIndex); - return index == MapTransform.NoMapping - ? new Pair() - : tuple.GetMappedContainer(index, isWriting); - } - - - // Constructors - - /// - /// Initializes new instance of this type. - /// - /// The transform. - /// Source tuple. - public MapTransformTuple1(MapTransform transform, Tuple source) - : base(transform) - { - tuple = source; - } - } -} \ No newline at end of file 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 fae607272e..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/MapTransformTuple3.cs +++ /dev/null @@ -1,102 +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 System.Collections.Generic; -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] - 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 - - /// - public override TupleFieldState GetFieldState(int fieldIndex) - { - var indexes = TypedTransform.map[fieldIndex]; - return tuples[indexes.First].GetFieldState(indexes.Second); - } - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - var indexes = TypedTransform.map[fieldIndex]; - tuples[indexes.First].SetFieldState(indexes.Second, fieldState); - } - - /// - public override object GetValue(int fieldIndex, out TupleFieldState fieldState) - { - Pair indexes = TypedTransform.map[fieldIndex]; - return tuples[indexes.First].GetValue(indexes.Second, out fieldState); - } - - /// - public override void SetValue(int fieldIndex, object fieldValue) - { - if (Transform.IsReadOnly) - throw Exceptions.ObjectIsReadOnly(null); - Pair indexes = TypedTransform.map[fieldIndex]; - tuples[indexes.First].SetValue(indexes.Second, fieldValue); - } - - #endregion - - protected internal override Pair GetMappedContainer(int fieldIndex, bool isWriting) - { - if (isWriting && Transform.IsReadOnly) - throw Exceptions.ObjectIsReadOnly(null); - var map = TypedTransform.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 FixedList3(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 FixedList3(source1, source2, source3); - } - } -} \ No newline at end of file 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 3cca08abf2..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/Internals/ReadOnlyTransformTuple.cs +++ /dev/null @@ -1,69 +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 System.Collections.Generic; -using Xtensive.Collections; -using Xtensive.Core; - - -namespace Xtensive.Tuples.Transform.Internals -{ - /// - /// A tuple wrapper for . - /// - [Serializable] - public sealed class ReadOnlyTransformTuple : WrappingTransformTupleBase - { - /// - public override ITupleTransform Transform { - get { - return ReadOnlyTransform.Instance; - } - } - - /// - /// - /// 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(); - } - } - - protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) - { - 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) - throw Exceptions.ObjectIsReadOnly(null); - return origin.GetMappedContainer(fieldIndex, isWriting); - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Tuple to provide read-only wrapper for. - public ReadOnlyTransformTuple(Tuple tuple) - : base(tuple) - { - } - } -} 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..1aad9129e8 --- /dev/null +++ b/Orm/Xtensive.Orm/Tuples/Transform/Internals/SegmentTransformTuple.cs @@ -0,0 +1,86 @@ +// 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 : Tuple, ITransformedTuple + { + private readonly SegmentTransform transform; + 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(transform.Descriptor); + + /// + public override TupleDescriptor Descriptor => transform.Descriptor; + + #region GetFieldState, GetValue, SetValue methods + + /// + public override TupleFieldState GetFieldState(int fieldIndex) + { + var index = GetSourceFieldIndex(fieldIndex); + return index == TransformUtil.NoMapping ? TupleFieldState.Default : source.GetFieldState(index); + } + + protected internal override void SetFieldState(int fieldIndex, TupleFieldState fieldState) + { + var index = GetSourceFieldIndex(fieldIndex); + if (index == TransformUtil.NoMapping) { + return; + } + source.SetFieldState(index, fieldState); + } + + /// + public override object GetValue(int fieldIndex, out TupleFieldState fieldState) + { + int index = GetSourceFieldIndex(fieldIndex); + return index == TransformUtil.NoMapping + ? DefaultResult.GetValue(fieldIndex, out fieldState) + : source.GetValue(index, out fieldState); + } + + /// + public override void SetValue(int fieldIndex, object fieldValue) + { + if (transform.IsReadOnly) { + throw Exceptions.ObjectIsReadOnly(null); + } + source.SetValue(GetSourceFieldIndex(fieldIndex), fieldValue); + } + + #endregion + + private int GetSourceFieldIndex(int 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) + { + this.transform = transform; + this.source = source; + } + } +} \ 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 afc03c40ea..d27152adc6 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/MapTransform.cs @@ -6,306 +6,104 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -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. + /// Maps fields of a destination tuple to the specified fields of of the source tuple. /// [Serializable] - public class MapTransform : TupleTransformBase + public sealed class MapTransform { - private readonly bool isReadOnly; - private int sourceCount; - internal IReadOnlyList singleSourceMap; - internal Pair[] map; + private readonly IReadOnlyList map; /// - /// Means that no mapping is available for the specified field index. + /// Gets describing the tuples + /// this transform may produce. /// - public const int NoMapping = int.MinValue; - - /// - public override bool IsReadOnly { - [DebuggerStepThrough] - get { return isReadOnly; } - } + public TupleDescriptor Descriptor { get; } /// - /// Gets the count of source this transform maps to the target one. - /// - public int SourceCount - { - [DebuggerStepThrough] - get { return sourceCount; } - } - + /// Indicates whether transform always produces read-only tuples or not. + /// > + public bool IsReadOnly { get; } + /// /// Gets or sets destination-to-source field map for the first source only. /// - public IReadOnlyList SingleSourceMap { - [DebuggerStepThrough] - get => singleSourceMap; - } - - /// - /// Gets or sets destination-to-source field map. - /// - public IReadOnlyList> Map - { - [DebuggerStepThrough] - get { return Array.AsReadOnly(map); } - } - - protected void SetSingleSourceMap(IReadOnlyList singleSourceMap) - { - ArgumentNullException.ThrowIfNull(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) - { - ArgumentNullException.ThrowIfNull(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; - } - - /// - public override Tuple Apply(TupleTransformType transformType, params object[] arguments) - { - ArgumentNullException.ThrowIfNull(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()); - } - } + public IReadOnlyList Map => map; /// /// Applies the transformation. /// /// The type of transformation to perform. - /// Transformation sources. + /// Transformation source. /// Transformation result - - /// either or descendant, + /// either instance or the descendant, /// dependently on specified . - public Tuple Apply(TupleTransformType transformType, params Tuple[] sources) + public Tuple Apply(TupleTransformType transformType, Tuple source) { - ArgumentNullException.ThrowIfNull(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) { + switch (transformType) { case TupleTransformType.Auto: - foreach (Tuple tuple in sources) - if (tuple is TransformedTuple) - goto case TupleTransformType.Tuple; + if (source is ITransformedTuple) + goto case TupleTransformType.Tuple; goto case TupleTransformType.TransformedTuple; case TupleTransformType.TransformedTuple: - return new MapTransformTuple(this, sources); + return new MapTransformTuple(this, source); case TupleTransformType.Tuple: Tuple result = Tuple.Create(Descriptor); - sources.CopyTo(result, map); + source.CopyTo(result, map); return result; default: - throw new ArgumentOutOfRangeException("transformType"); - } - } - } - - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// Transformation source. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - protected 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 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"); - } - } - - /// - /// Applies the transformation. - /// - /// The type of transformation to perform. - /// First transformation source. - /// Second transformation source. - /// Transformation result - - /// either or descendant, - /// dependently on specified . - protected 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 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"); - } - } - - /// - /// 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 . - protected 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 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"); + throw new ArgumentOutOfRangeException(nameof(transformType)); } } /// public override string ToString() { - string description = $"{SourceCount}: {(SourceCount == 1 ? singleSourceMap.ToCommaDelimitedString() : map.ToCommaDelimitedString())}, {(isReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, GetType().Name, description); + var description = $"[{map.ToCommaDelimitedString()}], {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + return string.Format(Strings.TupleTransformFormat, + nameof(MapTransform), + description); } - + // Constructors /// /// 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, 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) - { - SetSingleSourceMap(map); - } - - /// - /// Initializes a new instance of this type. - /// - /// property value. - /// Initial property value. - protected MapTransform(bool isReadOnly, TupleDescriptor descriptor) - : this(isReadOnly) { - ArgumentValidator.EnsureArgumentIsNotDefault(descriptor, nameof(descriptor)); + if (descriptor == default) + throw new ArgumentException("Argument is default instance.",nameof(descriptor)); + + IsReadOnly = isReadOnly; Descriptor = descriptor; - this.isReadOnly = isReadOnly; + + this.map = map ?? throw new ArgumentNullException(nameof(map)); } - + + + /// - /// Initializes a new instance of this type. + /// Initializes a new instance of this type with = . /// - /// property value. - protected MapTransform(bool isReadOnly) + /// The of the target . + /// property value. + // BE CAREFUL, NO VALIDATION OF PARAMETERS for the sake of performance + internal MapTransform(TupleDescriptor descriptor, IReadOnlyList map) { - this.isReadOnly = isReadOnly; + 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/ReadOnlyTransform.cs b/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs deleted file mode 100644 index abd8948f69..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/ReadOnlyTransform.cs +++ /dev/null @@ -1,82 +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; - } - } - - /// - public override Tuple Apply(TupleTransformType transformType, params object[] arguments) - { - ArgumentNullException.ThrowIfNull(arguments); - return Apply(transformType, arguments[0]); - } - - /// - /// Typed version of . - /// - /// 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("transformType"); - } - } - - - // Constructors - - private ReadOnlyTransform() - { - } - } -} \ 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 4f04b02d50..bf1faea3bd 100644 --- a/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs +++ b/Orm/Xtensive.Orm/Tuples/Transform/SegmentTransform.cs @@ -5,41 +5,63 @@ // Created: 2008.05.20 using System; -using System.Diagnostics; using Xtensive.Core; using Xtensive.Reflection; - - +using Xtensive.Tuples.Transform.Internals; namespace Xtensive.Tuples.Transform { /// /// Extracts specified from the . /// - public sealed class SegmentTransform : MapTransform + public sealed class SegmentTransform { - private Segment segment; + private readonly Segment segment; + + /// + public TupleDescriptor Descriptor { get; } + + /// + 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; } } - /// - public new Tuple Apply(TupleTransformType transformType, Tuple source) + /// + /// Applies the transformation. + /// + /// The type of transformation to perform. + /// Transformation source. + /// Transformation result - + /// either instance or the descendant, + /// dependently on specified . + public Tuple Apply(TupleTransformType transformType, Tuple source) { - return base.Apply(transformType, source); + 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; + } } /// public override string ToString() { - string description = $"{segment}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; - return string.Format(Strings.TupleTransformFormat, GetType().Name, description); + var description = $"{segment}, {(IsReadOnly ? Strings.ReadOnlyShort : Strings.ReadWriteShort)}"; + return string.Format(Strings.TupleTransformFormat, nameof(SegmentTransform), description); } @@ -48,21 +70,30 @@ 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) - : base(isReadOnly) { + 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; - 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/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/Transform/TransformedTuple.cs b/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs deleted file mode 100644 index 34a88c5e7d..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple.cs +++ /dev/null @@ -1,49 +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 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; } - } - - /// - public override string ToString() - { - return string.Format(Strings.TransformedTupleFormat, - base.ToString(), - Transform, - Arguments.ToCommaDelimitedString()); - } - } -} \ 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 deleted file mode 100644 index 54b7567ab1..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TransformedTuple{TTupleTransform}.cs +++ /dev/null @@ -1,51 +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 : TransformedTuple - where TTupleTransform : ITupleTransform - { - private TTupleTransform transform; - - /// - public override ITupleTransform Transform - { - [DebuggerStepThrough] - get { return transform; } - } - - /// - /// Gets or sets the transform used to produce this instance. - /// - public TTupleTransform TypedTransform { - get { return transform; } - protected set { - transform = value; - } - } - - - // Constructors - - /// - /// Initializes a new instance of this type. - /// - /// Tuple transform. - protected TransformedTuple(TTupleTransform transform) - { - this.transform = transform; - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs b/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs deleted file mode 100644 index 00e2419167..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/TupleTransformBase.cs +++ /dev/null @@ -1,57 +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; -using Xtensive.Reflection; - -namespace Xtensive.Tuples.Transform -{ - /// - /// Base class for any tuple transform. - /// - [Serializable] - public abstract class TupleTransformBase : ITupleTransform - { - private TupleDescriptor descriptor; - private Tuple defaultResult; - - /// - public TupleDescriptor Descriptor - { - get => descriptor; - protected set { - descriptor = value; - defaultResult = null; - } - } - - /// - public Tuple DefaultResult { - get { - if (defaultResult == null && descriptor != default) - defaultResult = Tuple.Create(descriptor).ToReadOnly(TupleTransformType.Tuple); - return defaultResult; - } - } - - /// - public virtual bool IsReadOnly { - [DebuggerStepThrough] - get { return false; } - } - - /// - public abstract Tuple Apply(TupleTransformType transformType, params object[] arguments); - - /// - public override string ToString() - { - var type = GetType(); - return type.IsGenericType ? type.GetShortName() : type.Name; - } - } -} \ 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 8ebb1b274a..0000000000 --- a/Orm/Xtensive.Orm/Tuples/Transform/WrappingTransformTupleBase.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: 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) - { - origin = tuple ?? throw new ArgumentNullException(nameof(tuple)); - } - } -} \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs b/Orm/Xtensive.Orm/Tuples/TupleDescriptor.cs index 3bbc924deb..5ce635cc2b 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; } - #region IList members + [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]; } } @@ -69,20 +68,83 @@ 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) + { + 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); + } + + /// + /// 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) + { + 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); + } + + /// + /// 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 fieldTypes = new Type[segment.Length]; + Array.Copy(this.fieldTypes, segment.Offset, fieldTypes, 0, segment.Length); + + return new TupleDescriptor(fieldTypes); + } + + /// + /// 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 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); + } + #region IEquatable members, GetHashCode /// 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; } } @@ -98,7 +160,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; } @@ -119,11 +181,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); } @@ -134,7 +196,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()); } @@ -177,34 +239,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) @@ -254,7 +288,7 @@ public static TupleDescriptor Create() private TupleDescriptor(Type[] fieldTypes) { var fieldCount = fieldTypes.Length; - FieldTypes = fieldTypes; + this.fieldTypes = fieldTypes; FieldDescriptors = new PackedFieldDescriptor[fieldCount]; switch (fieldCount) { @@ -263,17 +297,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; } } @@ -283,14 +317,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]); } } } diff --git a/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs b/Orm/Xtensive.Orm/Tuples/TupleExtensions.cs index b6e382dae0..2c362192ab 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,70 +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) - { - 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 && target is PackedTuple packedTarget) { - 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". - public static void CopyTo(this FixedList3 sources, Tuple target, Pair[] map) - { - var haveSlowSource = false; - var packedSources = new FixedList3(); - - for (int i = 0; i < sources.Count; i++) { - if (sources[i] is PackedTuple packedSource) { - packedSources.Push(packedSource); - } - else { - haveSlowSource = true; - break; - } - } - - if (!haveSlowSource && target is PackedTuple packedTarget) { - Copy3TuplesWithMappingFast(packedSources, packedTarget, map); - return; - } - - Copy3TuplesWithMappingSlow(sources, target, map); - } - #endregion #region Transforms @@ -178,9 +113,9 @@ public static void CopyTo(this FixedList3 sources, Tuple target, PairThe 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, new[] { left.Descriptor, right.Descriptor }); + var transform = new ConcatTransform(left.Descriptor, right.Descriptor); return transform.Apply(TupleTransformType.TransformedTuple, left, right); } @@ -192,16 +127,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(tuple.Descriptor, segment); return transform.Apply(TupleTransformType.TransformedTuple, tuple); } @@ -340,19 +266,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. /// @@ -485,50 +398,6 @@ private static void CopyTupleWithMappingFast(PackedTuple source, PackedTuple tar } } - private static void CopyTupleArrayWithMappingSlow(Tuple[] sources, Tuple target, Pair[] map) - { - for (int targetIndex = 0, length = map.Length; targetIndex < 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, length = map.Length; targetIndex < 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(FixedList3 sources, Tuple target, Pair[] map) - { - for (int targetIndex = 0, length = map.Length; targetIndex < 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(FixedList3 sources, PackedTuple target, Pair[] map) - { - for (int targetIndex = 0, length = map.Length; targetIndex < 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;