From 83d4b233ad58360a2109ca1b427119cfcabb4fa5 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 22 Aug 2022 19:35:02 -0700 Subject: [PATCH 1/8] Optimizations of Domain build (#83) * Optimize TypeInfo.Validators * Optimization: create FieldInfo.Assocations on demand * Avoid unnecessary .ToList() in TopologicalSorter.Sort() * DO_SAFE_COLLECTION_WRAPPER constant * readonly struct ColumnIndexMap * Optimize IReadOnlyList in SelectProvider --- Directory.Build.props | 3 ++ .../Helpers/TopologicalSorterTest.cs | 4 +- .../Core/Extensions/ListExtensions.cs | 24 ++++++++- Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs | 17 +++--- .../Linq/ExpressionVisitor{TResult}.cs | 5 +- .../Building/Builders/AssociationBuilder.cs | 10 ++-- .../Orm/Building/Builders/IndexBuilder.cs | 4 +- .../Orm/Building/Builders/ModelBuilder.cs | 8 +-- .../Orm/Building/Builders/TypeBuilder.cs | 31 ++++++----- .../Orm/Building/BuildingContext.cs | 8 +-- .../Orm/Building/Definitions/FieldDef.cs | 3 +- .../Orm/Building/Definitions/TypeDef.cs | 4 +- .../Prefetch/Nodes/IHasNestedNodes.cs | 4 +- .../Prefetch/Nodes/KeyExtractorNode.cs | 10 ++-- .../Prefetch/Nodes/NodeAggregator.cs | 7 +-- .../Internals/Prefetch/Nodes/NodeBuilder.cs | 6 +-- .../Internals/Prefetch/Nodes/NodeVisitor.cs | 8 +-- .../Internals/Prefetch/Nodes/ReferenceNode.cs | 10 ++-- .../Orm/Internals/Prefetch/Nodes/SetNode.cs | 6 +-- .../Orm/Linq/Translator.Materialization.cs | 5 +- Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs | 19 +++---- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 54 ++++++++++--------- Orm/Xtensive.Orm/Orm/Model/HierarchyInfo.cs | 11 ++-- Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs | 14 ++--- .../Orm/Model/PartialIndexFilterInfo.cs | 7 ++- .../Orm/Model/TypeIndexInfoCollection.cs | 9 ++-- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 22 +++----- Orm/Xtensive.Orm/Orm/Providers/Persister.cs | 7 ++- .../Requests/PersistRequestBuilder.cs | 7 +-- .../Orm/Providers/SqlCompiler.Index.cs | 2 +- .../Providers/Compilable/SelectProvider.cs | 12 +---- .../Transformation/ColumnMappingInspector.cs | 28 +++++----- .../Transformation/RedundantColumnRemover.cs | 4 +- Orm/Xtensive.Orm/Orm/StorageNode.cs | 3 +- .../Reflection/InterfaceMapping.cs | 5 +- Orm/Xtensive.Orm/Reflection/TypeHelper.cs | 8 +-- Orm/Xtensive.Orm/Sorting/Node.cs | 17 +++--- Orm/Xtensive.Orm/Sorting/TopologicalSorter.cs | 8 +-- 38 files changed, 209 insertions(+), 205 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5b17ca697b..f36acfd7a1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -128,4 +128,7 @@ + + $(DefineConstants);DO_SAFE_COLLECTION_WRAPPER + diff --git a/Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs b/Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs index 510fa970e8..ff3189c2a8 100644 --- a/Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs +++ b/Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs @@ -63,7 +63,7 @@ private static void InternalPerformanceTest(int nodeCount, int averageConnection List> removedEdges; var result = TopologicalSorter.Sort(nodes, out removedEdges); if (!allowLoops) - Assert.AreEqual(nodeCount, result.Count); + Assert.AreEqual(nodeCount, result.Count()); } GC.GetTotalMemory(true); } @@ -132,7 +132,7 @@ public void CombinedTest() private void TestSort(T[] data, Predicate connector, T[] expected, T[] loops) { List> actualLoopNodes; - List actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes); + List actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes).ToList(); T[] actualLoops = null; if (actualLoopNodes != null) actualLoops = actualLoopNodes diff --git a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs index d50fdcb846..bdc7c7b1e2 100644 --- a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs +++ b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs @@ -8,7 +8,7 @@ using System; using System.Collections; using System.Collections.Generic; - +using System.Runtime.CompilerServices; namespace Xtensive.Core { @@ -119,5 +119,25 @@ public static void EnsureIndexIsValid(this IList list, int index) if (index < 0 || index >= list.Count) throw new IndexOutOfRangeException(Strings.ExIndexOutOfRange); } + +#if DO_SAFE_COLLECTION_WRAPPER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this List list) => list.AsReadOnly(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this IReadOnlyList list) => new ReadOnlyCollection(list); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this T[] array) => Array.AsReadOnly(array); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this List list) => list; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this IReadOnlyList list) => list; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this T[] array) => array; +#endif } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs b/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs index 2262333a8b..6412d3e43d 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq.Expressions; +using Xtensive.Core; namespace Xtensive.Linq { @@ -17,7 +18,7 @@ namespace Xtensive.Linq /// public abstract class ExpressionVisitor : ExpressionVisitor { - protected override ReadOnlyCollection VisitExpressionList(ReadOnlyCollection expressions) + protected override IReadOnlyList VisitExpressionList(ReadOnlyCollection expressions) { bool isChanged = false; var results = new List(expressions.Count); @@ -27,7 +28,7 @@ protected override ReadOnlyCollection VisitExpressionList(ReadOnlyCo results.Add(p); isChanged |= !ReferenceEquals(expression, p); } - return isChanged ? results.AsReadOnly() : expressions; + return isChanged ? results.AsSafeWrapper() : expressions; } /// @@ -37,8 +38,8 @@ protected override ReadOnlyCollection VisitExpressionList(ReadOnlyCo /// Visit result. protected virtual ElementInit VisitElementInitializer(ElementInit initializer) { - ReadOnlyCollection arguments = VisitExpressionList(initializer.Arguments); - if (arguments!=initializer.Arguments) { + var arguments = VisitExpressionList(initializer.Arguments); + if (arguments != initializer.Arguments) { return Expression.ElementInit(initializer.AddMethod, arguments); } return initializer; @@ -49,7 +50,7 @@ protected virtual ElementInit VisitElementInitializer(ElementInit initializer) /// /// The original element initializer list. /// Visit result. - protected virtual ReadOnlyCollection VisitElementInitializerList(ReadOnlyCollection original) + protected virtual IReadOnlyList VisitElementInitializerList(ReadOnlyCollection original) { var results = new List(); bool isChanged = false; @@ -59,7 +60,7 @@ protected virtual ReadOnlyCollection VisitElementInitializerList(Re results.Add(p); isChanged |= !ReferenceEquals(originalIntializer, p); } - return isChanged ? results.AsReadOnly() : original; + return isChanged ? results.AsSafeWrapper() : original; } /// @@ -246,7 +247,7 @@ protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBindi /// /// The original binding list. /// Visit result. - protected virtual ReadOnlyCollection VisitBindingList(ReadOnlyCollection original) + protected virtual IReadOnlyList VisitBindingList(ReadOnlyCollection original) { var results = new List(); bool isChanged = false; @@ -256,7 +257,7 @@ protected virtual ReadOnlyCollection VisitBindingList(ReadOnlyCol results.Add(p); isChanged |= !ReferenceEquals(originalBinding, p); } - return isChanged ? results.AsReadOnly() : original; + return isChanged ? results.AsSafeWrapper() : original; } protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) diff --git a/Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs b/Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs index 54ddfc3726..4c50e3a985 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs @@ -9,6 +9,7 @@ using System.Collections.ObjectModel; using System.Linq.Expressions; using Xtensive.Reflection; +using Xtensive.Core; @@ -139,14 +140,14 @@ protected virtual TResult Visit(Expression e) /// /// The expression list. /// Visit result. - protected virtual ReadOnlyCollection VisitExpressionList(ReadOnlyCollection expressions) + protected virtual IReadOnlyList VisitExpressionList(ReadOnlyCollection expressions) { var results = new List(expressions.Count); for (int i = 0, n = expressions.Count; i < n; i++) { var p = Visit(expressions[i]); results.Add(p); } - return results.AsReadOnly(); + return results.AsSafeWrapper(); } /// diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs index db882628e5..e08a5538a0 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs @@ -25,7 +25,7 @@ public static void BuildAssociation(BuildingContext context, FieldDef fieldDef, fieldDef.OnOwnerRemove, fieldDef.OnTargetRemove); association.Name = context.NameBuilder.BuildAssociationName(association); context.Model.Associations.Add(association); - field.Associations.Add(association); + field.AddAssociation(association); if (!fieldDef.PairTo.IsNullOrEmpty()) context.PairedAssociations.Add(new Pair(association, fieldDef.PairTo)); @@ -40,8 +40,8 @@ public static void BuildAssociation(BuildingContext context, AssociationInfo ori .Where(a => a.TargetType == association.TargetType) .ToList(); foreach (var toRemove in associationsToRemove) - field.Associations.Remove(toRemove); - field.Associations.Add(association); + field.RemoveAssociation(toRemove); + field.AddAssociation(association); var pairTo = context.PairedAssociations.Where(p => p.First==origin).FirstOrDefault(); if (pairTo.First!=null) @@ -86,9 +86,9 @@ public static void BuildReversedAssociation(BuildingContext context, Association .Where(a => a.TargetType == association.TargetType) .ToList(); foreach (var toRemove in associationsToRemove) - field.Associations.Remove(toRemove); + field.RemoveAssociation(toRemove); - field.Associations.Add(association); + field.AddAssociation(association); } } diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs index 712a8fd541..e1ff88531b 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs @@ -551,7 +551,7 @@ private IndexInfo BuildFilterIndex(TypeInfo reflectedType, IndexInfo indexToFilt & (IndexAttributes.Primary | IndexAttributes.Secondary | IndexAttributes.Unique | IndexAttributes.Abstract) | IndexAttributes.Filtered | IndexAttributes.Virtual; var result = new IndexInfo(reflectedType, attributes, indexToFilter, Array.Empty()) { - FilterByTypes = filterByTypes.ToList().AsReadOnly() + FilterByTypes = filterByTypes.ToList().AsSafeWrapper() }; // Adding key columns @@ -778,7 +778,7 @@ private IndexInfo BuildViewIndex(TypeInfo reflectedType, IndexInfo indexToApplyV } result.ValueColumns.AddRange(valueColumns); - result.SelectColumns = columnMap.AsReadOnly(); + result.SelectColumns = columnMap.AsSafeWrapper(); result.Name = nameBuilder.BuildIndexName(reflectedType, result); result.Group = BuildColumnGroup(result); diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs index 9edf3ce881..4582d71ec4 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs @@ -277,7 +277,7 @@ private void PreprocessAssociations() foreach (var association in associationsToRemove) { context.Model.Associations.Remove(association); - refField.Associations.Remove(association); + refField.RemoveAssociation(association); } foreach (var association in associationsToKeep) { var interfaceAssociationsToRemove = interfaceAssociations @@ -287,10 +287,10 @@ private void PreprocessAssociations() foreach (var interfaceAssociation in interfaceAssociationsToRemove) interfaceAssociations.Remove(interfaceAssociation); } - refField.Associations.AddRange(interfaceAssociations); + refField.AddAssociations(interfaceAssociations); foreach (var association in inheritedAssociations) { - if (!refField.Associations.Contains(association.Name)) - refField.Associations.Add(association); + if (!refField.ContainsAssociation(association.Name)) + refField.AddAssociation(association); if (!context.Model.Associations.Contains(association)) context.Model.Associations.Add(association); } diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs index 92c9673937..390bd4229e 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs @@ -39,6 +39,11 @@ public TypeInfo BuildType(TypeDef typeDef) { using (BuildLog.InfoRegion(nameof(Strings.LogBuildingX), typeDef.UnderlyingType.GetShortName())) { + var validators = typeDef.Validators; + if (typeDef.IsEntity && DeclaresOnValidate(typeDef.UnderlyingType)) { + validators.Add(new EntityValidator()); + } + var typeInfo = new TypeInfo(context.Model, typeDef.Attributes) { UnderlyingType = typeDef.UnderlyingType, Name = typeDef.Name, @@ -46,13 +51,9 @@ public TypeInfo BuildType(TypeDef typeDef) MappingDatabase = typeDef.MappingDatabase, MappingSchema = typeDef.MappingSchema, HasVersionRoots = typeDef.UnderlyingType.GetInterfaces().Any(type => type == typeof(IHasVersionRoots)), - Validators = typeDef.Validators, + Validators = validators, }; - if (typeInfo.IsEntity && DeclaresOnValidate(typeInfo.UnderlyingType)) { - typeInfo.Validators.Add(new EntityValidator()); - } - if (typeDef.StaticTypeId != null) { typeInfo.TypeId = typeDef.StaticTypeId.Value; } @@ -232,6 +233,16 @@ private FieldInfo BuildDeclaredField(TypeInfo type, FieldDef fieldDef) { BuildLog.Info(nameof(Strings.LogBuildingDeclaredFieldXY), type.Name, fieldDef.Name); + var validators = fieldDef.Validators; + + if (fieldDef.IsStructure && DeclaresOnValidate(fieldDef.ValueType)) { + validators.Add(new StructureFieldValidator()); + } + + if (fieldDef.IsEntitySet && DeclaresOnValidate(fieldDef.ValueType)) { + validators.Add(new EntitySetFieldValidator()); + } + var fieldInfo = new FieldInfo(type, fieldDef.Attributes) { UnderlyingProperty = fieldDef.UnderlyingProperty, Name = fieldDef.Name, @@ -242,17 +253,9 @@ private FieldInfo BuildDeclaredField(TypeInfo type, FieldDef fieldDef) Length = fieldDef.Length, Scale = fieldDef.Scale, Precision = fieldDef.Precision, - Validators = fieldDef.Validators, + Validators = validators, }; - if (fieldInfo.IsStructure && DeclaresOnValidate(fieldInfo.ValueType)) { - fieldInfo.Validators.Add(new StructureFieldValidator()); - } - - if (fieldInfo.IsEntitySet && DeclaresOnValidate(fieldInfo.ValueType)) { - fieldInfo.Validators.Add(new EntitySetFieldValidator()); - } - type.Fields.Add(fieldInfo); if (fieldInfo.IsEntitySet) { diff --git a/Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs b/Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs index 7921126610..5b8fd3ab88 100644 --- a/Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs +++ b/Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs @@ -62,12 +62,12 @@ public sealed class BuildingContext /// /// Gets all available implementations. /// - public ICollection Modules { get; private set; } + public IReadOnlyList Modules { get; } /// /// Gets all available implementations. /// - public ICollection Modules2 { get; private set; } + public IReadOnlyList Modules2 { get; } internal ModelDefBuilder ModelDefBuilder { get; set; } @@ -85,8 +85,8 @@ internal BuildingContext(DomainBuilderConfiguration builderConfiguration) ModelInspectionResult = new ModelInspectionResult(); DependencyGraph = new Graph(); - Modules = BuilderConfiguration.Services.Modules.ToList().AsReadOnly(); - Modules2 = Modules.OfType().ToList().AsReadOnly(); + Modules = BuilderConfiguration.Services.Modules.AsSafeWrapper(); + Modules2 = Modules.OfType().ToList().AsSafeWrapper(); Validator = new Validator(builderConfiguration.Services.ProviderInfo.SupportedTypes); DefaultSchemaInfo = builderConfiguration.DefaultSchemaInfo; } diff --git a/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs b/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs index 7c5418e1c7..9ada902b7d 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs @@ -287,7 +287,7 @@ public string PairTo /// /// Gets of instances associated with this field. /// - public IList Validators { get; private set; } + public List Validators { get; } = new(); internal bool IsDeclaredAsNullable { @@ -325,7 +325,6 @@ internal FieldDef(Type valueType, Validator validator) if ((valueType.IsClass || valueType.IsInterface) && !IsStructure) attributes |= FieldAttributes.Nullable; ValueType = valueType; - Validators = new List(); // Nullable if (valueType.IsNullable()) { diff --git a/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs b/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs index 1e8af2638f..75bf6323ad 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs @@ -119,7 +119,7 @@ public NodeCollection Implementors /// /// Gets instances associated with this type. /// - public IList Validators { get; private set; } + public List Validators { get; } = new(); /// /// Gets or sets the type discriminator value. @@ -225,8 +225,6 @@ internal TypeDef(ModelDefBuilder builder, Type type, Validator validator) implementors = IsInterface ? new NodeCollection(this, "Implementors") : NodeCollection.Empty; - - Validators = new List(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/IHasNestedNodes.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/IHasNestedNodes.cs index 50d1354035..283995835e 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/IHasNestedNodes.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/IHasNestedNodes.cs @@ -11,10 +11,10 @@ namespace Xtensive.Orm.Internals.Prefetch { internal interface IHasNestedNodes { - ReadOnlyCollection NestedNodes { get; } + IReadOnlyList NestedNodes { get; } IReadOnlyCollection ExtractKeys(object target); - IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection nestedNodes); + IHasNestedNodes ReplaceNestedNodes(IReadOnlyList nestedNodes); } } diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/KeyExtractorNode.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/KeyExtractorNode.cs index 9b3af6a0e6..9eb36884c4 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/KeyExtractorNode.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/KeyExtractorNode.cs @@ -15,7 +15,7 @@ internal class KeyExtractorNode : Node, IHasNestedNodes { public Func> KeyExtractor { get; } - public ReadOnlyCollection NestedNodes { get; } + public IReadOnlyList NestedNodes { get; } IReadOnlyCollection IHasNestedNodes.ExtractKeys(object target) { @@ -27,10 +27,8 @@ public IReadOnlyCollection ExtractKeys(T target) return KeyExtractor.Invoke(target); } - public IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection nestedNodes) - { - return new KeyExtractorNode(KeyExtractor, nestedNodes); - } + public IHasNestedNodes ReplaceNestedNodes(IReadOnlyList nestedNodes) => + new KeyExtractorNode(KeyExtractor, nestedNodes); public override Node Accept(NodeVisitor visitor) { @@ -42,7 +40,7 @@ protected override string GetDescription() return $"KeyExtraction<{typeof(T).Name}>"; } - public KeyExtractorNode(Func> extractor, ReadOnlyCollection nestedNodes) + public KeyExtractorNode(Func> extractor, IReadOnlyList nestedNodes) : base("*") { ArgumentValidator.EnsureArgumentNotNull(extractor, nameof(extractor)); diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeAggregator.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeAggregator.cs index d239cce2a7..f5a7c1efbd 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeAggregator.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeAggregator.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Xtensive.Core; namespace Xtensive.Orm.Internals.Prefetch { @@ -26,7 +27,7 @@ public static IList> Aggregate(IEnumerable VisitNodeList(ReadOnlyCollection nodes) + public override IReadOnlyList VisitNodeList(IReadOnlyList nodes) { var result = new List(); foreach (var group in nodes.Where(n => n!=null).GroupBy(n => n.Path)) { @@ -36,11 +37,11 @@ public override ReadOnlyCollection VisitNodeList(ReadOnlyCollecti result.Add(node); else { var nodeToVisit = (BaseFieldNode) container.ReplaceNestedNodes( - new ReadOnlyCollection(group.Cast().SelectMany(c => c.NestedNodes).ToList())); + group.Cast().SelectMany(c => c.NestedNodes).ToList().AsSafeWrapper()); result.Add((BaseFieldNode) Visit(nodeToVisit)); } } - return new ReadOnlyCollection(result); + return result.AsSafeWrapper(); } // Constructor diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeBuilder.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeBuilder.cs index ebc5ad3fad..d0321d4432 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2020 Xtensive LLC. +// Copyright (C) 2012-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: Denis Krjuchkov @@ -119,9 +119,9 @@ private IEnumerable VisitMemberAccess(MemberExpression access) return EnumerableUtils.One(result); } - private static ObjectModel.ReadOnlyCollection WrapNodes(IEnumerable nodes) + private static IReadOnlyList WrapNodes(IEnumerable nodes) { - return nodes.ToList().AsReadOnly(); + return nodes.ToList().AsSafeWrapper(); } private IEnumerable VisitChildren(Expression expression) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeVisitor.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeVisitor.cs index ce371ec875..ec98bc9a94 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeVisitor.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/NodeVisitor.cs @@ -4,7 +4,9 @@ // Created by: Alexis Kochetov // Created: 2011.01.14 +using System.Collections.Generic; using System.Collections.ObjectModel; +using Xtensive.Core; namespace Xtensive.Orm.Internals.Prefetch { @@ -17,7 +19,7 @@ public virtual Node Visit(Node node) return node.Accept(this); } - public virtual ReadOnlyCollection VisitNodeList(ReadOnlyCollection nodes) + public virtual IReadOnlyList VisitNodeList(IReadOnlyList nodes) { BaseFieldNode[] list = null; var index = 0; @@ -35,9 +37,7 @@ public virtual ReadOnlyCollection VisitNodeList(ReadOnlyCollectio } index++; } - return list==null - ? nodes - : new ReadOnlyCollection(list); + return list?.AsSafeWrapper() ?? nodes; } public virtual Node VisitKeyExtractorNode(KeyExtractorNode keyExtractorNode) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/ReferenceNode.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/ReferenceNode.cs index 5bceb8dc84..13577e3796 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/ReferenceNode.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/ReferenceNode.cs @@ -15,7 +15,7 @@ internal sealed class ReferenceNode : BaseFieldNode, IHasNestedNodes { public TypeInfo ReferenceType { get; private set; } - public ReadOnlyCollection NestedNodes { get; private set; } + public IReadOnlyList NestedNodes { get; private set; } public IReadOnlyCollection ExtractKeys(object target) { @@ -30,10 +30,8 @@ public IReadOnlyCollection ExtractKeys(object target) : new[] {referenceKey}; } - public IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection nestedNodes) - { - return new ReferenceNode(Path, Field, ReferenceType, NestedNodes); - } + public IHasNestedNodes ReplaceNestedNodes(IReadOnlyList nestedNodes) => + new ReferenceNode(Path, Field, ReferenceType, NestedNodes); public override Node Accept(NodeVisitor visitor) { @@ -42,7 +40,7 @@ public override Node Accept(NodeVisitor visitor) // Constructors - public ReferenceNode(string path, FieldInfo field, TypeInfo referenceType, ReadOnlyCollection nestedNodes) + public ReferenceNode(string path, FieldInfo field, TypeInfo referenceType, IReadOnlyList nestedNodes) : base(path, field) { ReferenceType = referenceType; diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/SetNode.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/SetNode.cs index 5650ef97a9..8d00ff0ac4 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/SetNode.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/Nodes/SetNode.cs @@ -14,7 +14,7 @@ namespace Xtensive.Orm.Internals.Prefetch { internal class SetNode : BaseFieldNode, IHasNestedNodes { - public ReadOnlyCollection NestedNodes { get; } + public IReadOnlyList NestedNodes { get; } public TypeInfo ElementType { get; } @@ -30,7 +30,7 @@ public IReadOnlyCollection ExtractKeys(object target) return fetchedKeys.ToArray(fetchedKeys.Count); } - public IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection nestedNodes) => + public IHasNestedNodes ReplaceNestedNodes(IReadOnlyList nestedNodes) => new SetNode(Path, Field, ElementType, NestedNodes); public override Node Accept(NodeVisitor visitor) => visitor.VisitSetNode(this); @@ -38,7 +38,7 @@ public IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection nest // Constructors - public SetNode(string path, FieldInfo field, TypeInfo elementType, ReadOnlyCollection nestedNodes) + public SetNode(string path, FieldInfo field, TypeInfo elementType, IReadOnlyList nestedNodes) : base(path, field) { ElementType = elementType; diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs index 0da819fec2..080980bc39 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs @@ -89,9 +89,8 @@ private static ProjectionExpression Optimize(ProjectionExpression origin) if (usedColumns.Count == 0) usedColumns.Add(0); if (usedColumns.Count < origin.ItemProjector.DataSource.Header.Length) { - var usedColumnsArray = usedColumns.ToArray(); - var resultProvider = new SelectProvider(originProvider, usedColumnsArray); - var itemProjector = origin.ItemProjector.Remap(resultProvider, usedColumnsArray); + var resultProvider = new SelectProvider(originProvider, usedColumns); + var itemProjector = origin.ItemProjector.Remap(resultProvider, usedColumns); var result = origin.Apply(itemProjector); return result; } diff --git a/Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs b/Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs index eeab313606..d2f14453ae 100644 --- a/Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs +++ b/Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using Xtensive.Core; namespace Xtensive.Orm.Model @@ -14,22 +15,22 @@ namespace Xtensive.Orm.Model /// A map of useful column indexes. /// [Serializable] - public sealed class ColumnIndexMap + public readonly struct ColumnIndexMap { /// /// Gets or sets positions of system columns within . /// - public IList System { get; private set; } + public IReadOnlyList System { get; } /// /// Gets or sets positions of lazy load columns within . /// - public IList LazyLoad { get; private set; } + public IReadOnlyList LazyLoad { get; } /// /// Gets or sets positions of regular columns (not system and not lazy load) within . /// - public IList Regular { get; private set; } + public IReadOnlyList Regular { get; } // Constructors @@ -40,11 +41,11 @@ public sealed class ColumnIndexMap /// The system columns. /// The regular columns. /// The lazy load columns. - public ColumnIndexMap(List system, List regular, List lazyLoad) + public ColumnIndexMap(IReadOnlyList system, IReadOnlyList regular, IReadOnlyList lazyLoad) { - System = system.AsReadOnly(); - LazyLoad = lazyLoad.AsReadOnly(); - Regular = regular.AsReadOnly(); + System = system.AsSafeWrapper(); + LazyLoad = lazyLoad.AsSafeWrapper(); + Regular = regular.AsSafeWrapper(); } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index b15369e0e1..a1371b8302 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -60,7 +60,6 @@ public sealed class FieldInfo : MappedNode, private int fieldId; private int? cachedHashCode; - private IList validators; private Segment mappingInfo; #region IsXxx properties @@ -545,11 +544,12 @@ public ColumnInfo Column /// public AssociationInfo GetAssociation(TypeInfo targetType) { - if (associations.Count == 0) - return null; - - if (associations.Count == 1) - return associations[0]; + switch (Associations.Count) { + case 0: + return null; + case 1: + return associations[0]; + } var ordered = IsLocked ? associations @@ -559,11 +559,26 @@ public AssociationInfo GetAssociation(TypeInfo targetType) a => a.TargetType.UnderlyingType.IsAssignableFrom(targetType.UnderlyingType)); } - public NodeCollection Associations + public IReadOnlyList Associations => (IReadOnlyList)associations ?? Array.Empty(); + + public bool ContainsAssociation(string associationName) => associations?.Contains(associationName) == true; + + public void AddAssociation(AssociationInfo association) => + EnsureAssociations().Add(association); + + public void AddAssociations(IReadOnlyList range) { - get { return associations; } + if (range.Count > 0) { + EnsureAssociations().AddRange(range); + } } + public void RemoveAssociation(AssociationInfo association) => + _ = EnsureAssociations().Remove(association); + + private NodeCollection EnsureAssociations() => + associations ??= new NodeCollection(this, "Associations"); + /// /// Gets or sets field's adapter index. /// @@ -582,14 +597,7 @@ public int AdapterIndex /// Gets instances /// associated with this field. /// - public IList Validators - { - get { return validators; } - internal set { - EnsureNotLocked(); - validators = value; - } - } + public IReadOnlyList Validators { get; init; } /// /// Gets value indicating if this field @@ -658,7 +666,7 @@ public override void UpdateState() columns?.Clear(); // To prevent event handler leak columns = null; - HasImmediateValidators = validators.Count > 0 && validators.Any(v => v.IsImmediate); + HasImmediateValidators = Validators.Count > 0 && Validators.Any(v => v.IsImmediate); CreateMappingInfo(); } @@ -669,16 +677,15 @@ public override void Lock(bool recursive) base.Lock(recursive); if (!recursive) return; - validators = Array.AsReadOnly(validators.ToArray()); Fields.Lock(true); if (column != null) column.Lock(true); - if (associations.Count > 1) { + if (Associations.Count > 1) { var sorted = associations.Reorder(); associations = new NodeCollection(associations.Owner, associations.Name); associations.AddRange(sorted); } - associations.Lock(false); + associations?.Lock(false); } private void CreateMappingInfo() @@ -778,9 +785,9 @@ public FieldInfo Clone() defaultValue = defaultValue, defaultSqlExpression = defaultSqlExpression, DeclaringField = DeclaringField, - Validators = Validators.Select(v => v.CreateNew()).ToList(), + Validators = Validators.Select(v => v.CreateNew()).ToArray(), }; - clone.Associations.AddRange(associations); + clone.AddAssociations(Associations); return clone; } @@ -808,7 +815,6 @@ private FieldInfo(TypeInfo declaringType, TypeInfo reflectedType, FieldAttribute Fields = IsEntity || IsStructure ? new FieldInfoCollection(this, "Fields") : FieldInfoCollection.Empty; - associations = new NodeCollection(this, "Associations"); } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Model/HierarchyInfo.cs b/Orm/Xtensive.Orm/Orm/Model/HierarchyInfo.cs index 100754dfd5..7f322d0b81 100644 --- a/Orm/Xtensive.Orm/Orm/Model/HierarchyInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/HierarchyInfo.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using Xtensive.Core; namespace Xtensive.Orm.Model { @@ -47,7 +48,7 @@ public override void UpdateState() Key.UpdateState(); var list = new List {Root}; list.AddRange(Root.AllDescendants); - Types = list.AsReadOnly(); + Types = list.AsSafeWrapper(); if (Types.Count == 1) InheritanceSchema = InheritanceSchema.ConcreteTable; if (TypeDiscriminatorMap != null) @@ -67,16 +68,16 @@ public override void Lock(bool recursive) // Constructors /// - /// Initializes a new instance of this class. + /// Initializes a new instance of this class. /// /// The hierarchy root. /// The key info. /// The inheritance schema. /// The type discriminator map. public HierarchyInfo( - TypeInfo root, - KeyInfo key, - InheritanceSchema inheritanceSchema, + TypeInfo root, + KeyInfo key, + InheritanceSchema inheritanceSchema, TypeDiscriminatorMap typeDiscriminatorMap) { Root = root; diff --git a/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs b/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs index 5156889784..1a5785c949 100644 --- a/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs @@ -35,11 +35,11 @@ public sealed class IndexInfo : MappedNode private readonly IndexInfo declaringIndex; private double fillFactor; private string shortName; - private ReadOnlyCollection columns; + private IReadOnlyList columns; private TupleDescriptor tupleDescriptor; private TupleDescriptor keyTupleDescriptor; - private IList filterByTypes; - private IList selectColumns; + private IReadOnlyList filterByTypes; + private IReadOnlyList selectColumns; private List>> valueColumnsMap; private LambdaExpression filterExpression; private PartialIndexFilterInfo filter; @@ -173,7 +173,7 @@ public IndexInfo DeclaringIndex /// /// Gets the types for index. /// - public IList FilterByTypes + public IReadOnlyList FilterByTypes { get { return filterByTypes; } set @@ -214,7 +214,7 @@ public PartialIndexFilterInfo Filter { /// /// Gets the column indexes for index. /// - public IList SelectColumns + public IReadOnlyList SelectColumns { get { return selectColumns; } set @@ -339,7 +339,7 @@ public override void UpdateState() var lazy = new List(); var regular = new List(); - for (int i = 0; i < columns.Count; i++) { + for (int i = 0, count = columns.Count; i < count; i++) { var item = columns[i]; if (item.IsPrimaryKey || item.IsSystem) system.Add(i); @@ -392,7 +392,7 @@ private void CreateColumns() { var result = new List(keyColumns.Select(pair => pair.Key)); result.AddRange(valueColumns); - columns = result.AsReadOnly(); + columns = result.AsSafeWrapper(); } diff --git a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs index 2363000d84..b37d3f18d9 100644 --- a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Xtensive LLC. +// Copyright (C) 2011 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Denis Krjuchkov @@ -32,12 +32,12 @@ public LambdaExpression Expression } } - private IList fields; + private IReadOnlyList fields; /// /// Fields used in . /// - public IList Fields + public IReadOnlyList Fields { get { return fields; } set @@ -56,7 +56,6 @@ public override void Lock(bool recursive) throw Exceptions.NotInitialized("Expression"); if (Fields==null) throw Exceptions.NotInitialized("Fields"); - fields = fields.ToList().AsReadOnly(); base.Lock(recursive); } } diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeIndexInfoCollection.cs b/Orm/Xtensive.Orm/Orm/Model/TypeIndexInfoCollection.cs index 2911d4b34c..8c51be8372 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeIndexInfoCollection.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeIndexInfoCollection.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Linq; using System.Collections.Generic; +using Xtensive.Core; namespace Xtensive.Orm.Model { @@ -39,7 +40,7 @@ public IReadOnlyList RealPrimaryIndexes get { return IsLocked ? realPrimaryIndexes - : FindRealPrimaryIndexes(PrimaryIndex).AsReadOnly(); + : FindRealPrimaryIndexes(PrimaryIndex).AsSafeWrapper(); } } @@ -76,8 +77,8 @@ public override void UpdateState() { base.UpdateState(); primaryIndex = FindPrimaryIndex(); - realPrimaryIndexes = FindRealPrimaryIndexes(primaryIndex).AsReadOnly(); - indexesContainingAllData = FindIndexesContainingAllData().AsReadOnly(); + realPrimaryIndexes = FindRealPrimaryIndexes(primaryIndex).AsSafeWrapper(); + indexesContainingAllData = FindIndexesContainingAllData().AsSafeWrapper(); } private IndexInfo GetIndex(IEnumerable fields) @@ -118,7 +119,7 @@ public IReadOnlyList GetIndexesContainingAllData() { return IsLocked ? indexesContainingAllData - : FindIndexesContainingAllData().AsReadOnly(); + : FindIndexesContainingAllData().AsSafeWrapper(); } private List FindIndexesContainingAllData() diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 86949ff6cf..aa2582112d 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -54,7 +54,6 @@ public sealed class TypeInfo : SchemaMappedNode private IReadOnlyList removalSequence; private IReadOnlyList versionFields; private IReadOnlyList versionColumns; - private IList validators; private Type underlyingType; private HierarchyInfo hierarchy; private int typeId = NoTypeId; @@ -528,14 +527,7 @@ public IDictionary, FieldInfo> StructureFieldMapping /// Gets instances /// associated with this type. /// - public IList Validators - { - get { return validators; } - internal set { - EnsureNotLocked(); - validators = value; - } - } + public IReadOnlyList Validators { get; init; } /// /// Gets value indicating if this type has validators (including field validators). @@ -618,7 +610,7 @@ public IEnumerable GetTargetAssociations() if (!IsLocked) { return result; } - targetAssociations = result.ToList().AsReadOnly(); + targetAssociations = result.ToList().AsSafeWrapper(); } return targetAssociations; } @@ -633,7 +625,7 @@ public IEnumerable GetOwnerAssociations() if (!IsLocked) { return result; } - ownerAssociations = result.ToList().AsReadOnly(); + ownerAssociations = result.ToList().AsSafeWrapper(); } return ownerAssociations; } @@ -745,7 +737,7 @@ public override void UpdateState() } } - HasValidators = validators.Count > 0 || fields.Any(f => f.HasValidators); + HasValidators = Validators.Count > 0 || fields.Any(static f => f.HasValidators); // Selecting master parts from paired associations & single associations var associations = model.Associations.Find(this) @@ -804,7 +796,7 @@ public override void UpdateState() var sortedRemovalSequence = sequence.Where(a => a.Ancestors.Count > 0).ToList(); if (sortedRemovalSequence.Count == 0) { - removalSequence = sequence.AsReadOnly(); + removalSequence = sequence.AsSafeWrapper(); } else { var sequenceSize = sequence.Count; @@ -812,7 +804,7 @@ public override void UpdateState() sortedRemovalSequence.Capacity = sequenceSize; } sortedRemovalSequence.AddRange(sequence.Where(a => a.Ancestors.Count == 0)); - removalSequence = sortedRemovalSequence.AsReadOnly(); + removalSequence = sortedRemovalSequence.AsSafeWrapper(); } } @@ -833,8 +825,6 @@ public override void Lock(bool recursive) if (!recursive) return; - validators = Array.AsReadOnly(validators.ToArray()); - directDescendants = directDescendants != null ? new Collections.ReadOnlyHashSet((HashSet) directDescendants) : EmptyTypes; diff --git a/Orm/Xtensive.Orm/Orm/Providers/Persister.cs b/Orm/Xtensive.Orm/Orm/Providers/Persister.cs index 6d6733d023..f36c539733 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Persister.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Persister.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2012 Xtensive LLC. +// Copyright (C) 2012 Xtensive LLC. // All rights reserved. // For conditions of distribution and use, see license. // Created by: Denis Krjuchkov @@ -93,11 +93,10 @@ private SqlPersistTask CreateRemoveTask(PersistAction action, bool validateVersi } } - private ICollection GetOrBuildRequest(StorageNode node, PersistRequestBuilderTask task) + private IReadOnlyList GetOrBuildRequest(StorageNode node, PersistRequestBuilderTask task) { var cache = node.PersistRequestCache; - ICollection result; - if (cache.TryGetValue(task, out result)) + if (cache.TryGetValue(task, out var result)) return result; result = requestBuilder.Build(node, task); return cache.TryAdd(task, result) ? result : cache[task]; diff --git a/Orm/Xtensive.Orm/Orm/Providers/Requests/PersistRequestBuilder.cs b/Orm/Xtensive.Orm/Orm/Providers/Requests/PersistRequestBuilder.cs index 239600f7c1..b4839b3f44 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/Requests/PersistRequestBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/Requests/PersistRequestBuilder.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using Xtensive.Core; using Xtensive.Orm.Configuration; using Xtensive.Orm.Model; using Xtensive.Sql; @@ -23,7 +24,7 @@ public class PersistRequestBuilder : DomainBoundHandler private ProviderInfo providerInfo; private StorageDriver driver; - internal ICollection Build(StorageNode node, PersistRequestBuilderTask task) + internal IReadOnlyList Build(StorageNode node, PersistRequestBuilderTask task) { var context = new PersistRequestBuilderContext(task, node.Mapping, node.Configuration); List result; @@ -52,14 +53,14 @@ internal ICollection Build(StorageNode node, PersistRequestBuild } var batchRequest = CreatePersistRequest(batch, bindings, node.Configuration); batchRequest.Prepare(); - return new List {batchRequest}.AsReadOnly(); + return new List { batchRequest }.AsSafeWrapper(); } foreach (var item in result) { item.Prepare(); } - return result.AsReadOnly(); + return result.AsSafeWrapper(); } protected virtual List BuildInsertRequest(PersistRequestBuilderContext context) diff --git a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs index dfd834e828..011aa614dc 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs @@ -230,7 +230,7 @@ private SqlSelect BuildTypedQuery(IndexInfo index) if (discriminatorMap != null) { var discriminatorColumnIndex = 0; var discriminatorColumnInfo = discriminatorMap.Column; - var underlyingColumns = underlyingIndex.Columns; + var underlyingColumns = underlyingIndex.Columns; for (var columnCount = underlyingColumns.Count; discriminatorColumnIndex < columnCount; discriminatorColumnIndex++) { var column = underlyingColumns[discriminatorColumnIndex]; if (column.Equals(discriminatorColumnInfo)) { diff --git a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs index 181e0054d4..5cfd06f4d0 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Providers/Compilable/SelectProvider.cs @@ -44,17 +44,7 @@ protected override string ParametersToString() public SelectProvider(CompilableProvider provider, IReadOnlyList columnIndexes) : base(ProviderType.Select, provider) { - switch (columnIndexes) { - case int[] indexArray: - ColumnIndexes = Array.AsReadOnly(indexArray); - break; - case List indexList: - ColumnIndexes = indexList.AsReadOnly(); - break; - default: - ColumnIndexes = columnIndexes; - break; - } + ColumnIndexes = columnIndexes.AsSafeWrapper(); Initialize(); } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs index 257716a6a2..d346556d93 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs @@ -78,7 +78,7 @@ protected override Provider VisitSelect(SelectProvider provider) mappings[provider] = newMappings; return source == provider.Source ? provider - : new SelectProvider(source, indexColumns.ToArray()); + : new SelectProvider(source, indexColumns); } /// @@ -122,7 +122,7 @@ protected override Provider VisitJoin(JoinProvider provider) { // split - SplitMappings(provider, out var leftMapping, out var rightMapping); + var (leftMapping, rightMapping) = SplitMappings(provider); leftMapping = Merge(leftMapping, provider.EqualIndexes.Select(p => p.First)); rightMapping = Merge(rightMapping, provider.EqualIndexes.Select(p => p.Second)); @@ -148,7 +148,7 @@ protected override Provider VisitJoin(JoinProvider provider) protected override Provider VisitPredicateJoin(PredicateJoinProvider provider) { - SplitMappings(provider, out var leftMapping, out var rightMapping); + var (leftMapping, rightMapping) = SplitMappings(provider); leftMapping.AddRange(mappingsGatherer.Gather(provider.Predicate, provider.Predicate.Parameters[0])); @@ -190,7 +190,7 @@ protected override Provider VisitApply(ApplyProvider provider) { // split - SplitMappings(provider, out var leftMapping, out var rightMapping); + var (leftMapping, rightMapping) = SplitMappings(provider); var applyParameter = provider.ApplyParameter; var currentOuterUsages = new List(); @@ -217,12 +217,12 @@ protected override Provider VisitApply(ApplyProvider provider) _ = outerColumnUsages.Remove(applyParameter); var pair = OverrideRightApplySource(provider, newRightProvider, rightMapping); - if (pair.First == null) { + if (pair.compilableProvider == null) { rightMapping = mappings[provider.Right]; } else { - newRightProvider = pair.First; - rightMapping = pair.Second; + newRightProvider = pair.compilableProvider; + rightMapping = pair.mapping; } RestoreMappings(oldMappings); @@ -400,9 +400,8 @@ private static CompilableProvider BuildSetOperationSource(CompilableProvider pro return new SelectProvider(provider, columns); } - protected virtual Pair> OverrideRightApplySource(ApplyProvider applyProvider, - CompilableProvider provider, List requestedMapping) => - new Pair>(provider, requestedMapping); + protected virtual (CompilableProvider compilableProvider, List mapping) OverrideRightApplySource(ApplyProvider applyProvider, CompilableProvider provider, List requestedMapping) => + (provider, requestedMapping); #endregion @@ -441,20 +440,21 @@ private static List MergeMappings(Provider originalLeft, List leftMap, return result; } - private void SplitMappings(BinaryProvider provider, out List leftMapping, out List rightMapping) + private (List leftMapping, List rightMapping) SplitMappings(BinaryProvider provider) { var binaryMapping = mappings[provider]; - leftMapping = new List(binaryMapping.Count); + var leftMapping = new List(binaryMapping.Count); var leftCount = provider.Left.Header.Length; var index = 0; while (index < binaryMapping.Count && binaryMapping[index] < leftCount) { leftMapping.Add(binaryMapping[index]); index++; } - rightMapping = new List(binaryMapping.Count - index); + var rightMapping = new List(binaryMapping.Count - index); for (var i = index; i < binaryMapping.Count; i++) { rightMapping.Add(binaryMapping[i] - leftCount); } + return (leftMapping, rightMapping); } private void RegisterOuterMapping(ApplyParameter parameter, int value) @@ -508,7 +508,7 @@ private void VisitJoin(ref List leftMapping, ref CompilableProvider left, r private Dictionary> ReplaceMappings(Provider firstNewKey, List firstNewValue) { var oldMappings = mappings; - mappings = new Dictionary> {{firstNewKey, firstNewValue}}; + mappings = new() { { firstNewKey, firstNewValue } }; return oldMappings; } diff --git a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs index 9ef551e717..39fe411f27 100644 --- a/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs +++ b/Orm/Xtensive.Orm/Orm/Rse/Transformation/RedundantColumnRemover.cs @@ -27,13 +27,13 @@ internal sealed class RedundantColumnRemover : ColumnMappingInspector && methodInfo.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == WellKnownTypes.FuncOfTArgTResultType) .CachedMakeGenericMethod(WellKnownOrmTypes.Tuple, WellKnownOrmTypes.Tuple); - protected override Pair> OverrideRightApplySource(ApplyProvider applyProvider, CompilableProvider provider, List requestedMapping) + protected override (CompilableProvider, List) OverrideRightApplySource(ApplyProvider applyProvider, CompilableProvider provider, List requestedMapping) { var currentMapping = mappings[applyProvider.Right]; if (currentMapping.SequenceEqual(requestedMapping)) return base.OverrideRightApplySource(applyProvider, provider, requestedMapping); var selectProvider = new SelectProvider(provider, requestedMapping.ToArray()); - return new Pair>(selectProvider, requestedMapping); + return (selectProvider, requestedMapping); } protected override Provider VisitRaw(RawProvider provider) diff --git a/Orm/Xtensive.Orm/Orm/StorageNode.cs b/Orm/Xtensive.Orm/Orm/StorageNode.cs index 20ac4d09b1..8c339a341e 100644 --- a/Orm/Xtensive.Orm/Orm/StorageNode.cs +++ b/Orm/Xtensive.Orm/Orm/StorageNode.cs @@ -72,7 +72,7 @@ public sealed class StorageNode : ISessionSource /// internal ConcurrentDictionary)> RefsToEntityQueryCache { get; } internal ConcurrentDictionary KeySequencesCache { get; } - internal ConcurrentDictionary> PersistRequestCache { get; } + internal ConcurrentDictionary> PersistRequestCache { get; } = new(); /// public Session OpenSession() => @@ -127,7 +127,6 @@ internal StorageNode(Domain domain, NodeConfiguration configuration, ModelMappin EntitySetTypeStateCache = new ConcurrentDictionary(); RefsToEntityQueryCache = new ConcurrentDictionary)>(); KeySequencesCache = new ConcurrentDictionary(); - PersistRequestCache = new ConcurrentDictionary>(); this.domain = domain; Configuration = configuration; diff --git a/Orm/Xtensive.Orm/Reflection/InterfaceMapping.cs b/Orm/Xtensive.Orm/Reflection/InterfaceMapping.cs index a2b3055a32..fe7e9730de 100644 --- a/Orm/Xtensive.Orm/Reflection/InterfaceMapping.cs +++ b/Orm/Xtensive.Orm/Reflection/InterfaceMapping.cs @@ -10,6 +10,7 @@ using ReflectionInterfaceMapping=System.Reflection.InterfaceMapping; using System.Linq; using System.Collections.Generic; +using Xtensive.Core; namespace Xtensive.Reflection { @@ -50,8 +51,8 @@ public InterfaceMapping(ReflectionInterfaceMapping source) { TargetType = source.TargetType; InterfaceType = source.InterfaceType; - TargetMethods = Array.AsReadOnly(source.TargetMethods); - InterfaceMethods = Array.AsReadOnly(source.InterfaceMethods); + TargetMethods = source.TargetMethods.AsSafeWrapper(); + InterfaceMethods = source.InterfaceMethods.AsSafeWrapper(); } } } \ No newline at end of file diff --git a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index 601d9b111f..31c8436250 100644 --- a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs +++ b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs @@ -703,8 +703,8 @@ from pair in zipped /// /// The types to sort. /// The list of ordered by their inheritance. - public static List OrderByInheritance(this IEnumerable types) => - TopologicalSorter.Sort(types, (t1, t2) => t1.IsAssignableFrom(t2)); + public static IEnumerable OrderByInheritance(this IEnumerable types) => + TopologicalSorter.Sort(types, static (t1, t2) => t1.IsAssignableFrom(t2)); /// /// Fast analogue of . @@ -723,7 +723,7 @@ public static InterfaceMapping GetInterfaceMapFast(this Type type, Type targetIn /// The type to get the interfaces of. [Obsolete("Use GetInterfacesOrderByInheritance instead")] public static Type[] GetInterfaces(this Type type) => - OrderedInterfaces.GetOrAdd(type, t => t.GetInterfaces().OrderByInheritance().ToArray()); + OrderedInterfaces.GetOrAdd(type, static t => t.GetInterfaces().OrderByInheritance().ToArray()); /// /// Gets the interfaces of the specified type. @@ -731,7 +731,7 @@ public static Type[] GetInterfaces(this Type type) => /// /// The type to get the interfaces of. public static Type[] GetInterfacesOrderByInheritance(this Type type) => - OrderedInterfaces.GetOrAdd(type, t => t.GetInterfaces().OrderByInheritance().ToArray()); + OrderedInterfaces.GetOrAdd(type, static t => t.GetInterfaces().OrderByInheritance().ToArray()); /// /// Gets the sequence of type itself, all its base types and interfaces. diff --git a/Orm/Xtensive.Orm/Sorting/Node.cs b/Orm/Xtensive.Orm/Sorting/Node.cs index e07541db0d..b0900e21b1 100644 --- a/Orm/Xtensive.Orm/Sorting/Node.cs +++ b/Orm/Xtensive.Orm/Sorting/Node.cs @@ -10,7 +10,6 @@ using System.Linq; using Xtensive.Core; - namespace Xtensive.Sorting { /// @@ -22,9 +21,9 @@ namespace Xtensive.Sorting public class Node { private List> incomingConnections; - private ReadOnlyCollection> incomingConnectionsReadOnlyList; + private IReadOnlyList> incomingConnectionsReadOnlyList; private List> outgoingConnections; - private ReadOnlyCollection> outgoingConnectionsReadOnlyList; + private IReadOnlyList> outgoingConnectionsReadOnlyList; /// /// Gets node item. @@ -164,18 +163,14 @@ public IEnumerable> RemoveConnections private void EnsureIncomingConnections() { - if (incomingConnectionsReadOnlyList==null) { - incomingConnections = new List>(); - incomingConnectionsReadOnlyList = incomingConnections.AsReadOnly(); - } + incomingConnectionsReadOnlyList ??= + (incomingConnections = new List>()).AsSafeWrapper(); } private void EnsureOutgoingConnections() { - if (outgoingConnectionsReadOnlyList==null) { - outgoingConnections = new List>(); - outgoingConnectionsReadOnlyList = outgoingConnections.AsReadOnly(); - } + outgoingConnectionsReadOnlyList ??= + (outgoingConnections = new List>()).AsSafeWrapper(); } // Constructors diff --git a/Orm/Xtensive.Orm/Sorting/TopologicalSorter.cs b/Orm/Xtensive.Orm/Sorting/TopologicalSorter.cs index 64566109dc..284b36011b 100644 --- a/Orm/Xtensive.Orm/Sorting/TopologicalSorter.cs +++ b/Orm/Xtensive.Orm/Sorting/TopologicalSorter.cs @@ -29,7 +29,7 @@ public static class TopologicalSorter /// Sorting result, if there were no loops; /// otherwise, . /// - public static List Sort(IEnumerable items, Predicate connector) + public static IEnumerable Sort(IEnumerable items, Predicate connector) { List> loops; return Sort(items, connector, out loops); @@ -48,7 +48,7 @@ public static List Sort(IEnumerable items, Pred /// otherwise, . /// In this case will contain only the loop edges. /// - public static List Sort(IEnumerable items, Predicate connector, out List> loops) + public static IEnumerable Sort(IEnumerable items, Predicate connector, out List> loops) { ArgumentValidator.EnsureArgumentNotNull(items, "items"); ArgumentValidator.EnsureArgumentNotNull(connector, "connector"); @@ -101,7 +101,7 @@ public static List Sort(IEnumerable items, Pred /// Sorting result, if there were no loops; /// otherwise, . /// In this case will contain only the loop edges. - public static List Sort(List> nodes, out List> loops) + public static IEnumerable Sort(List> nodes, out List> loops) { ArgumentValidator.EnsureArgumentNotNull(nodes, "nodes"); var head = new Queue(); @@ -136,7 +136,7 @@ public static List Sort(List Date: Tue, 25 Jul 2023 11:23:00 -0700 Subject: [PATCH 2/8] Optimize FieldInfo, AssociationInfo classes --- .../Core/Extensions/ListExtensions.cs | 3 ++- Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs | 20 ++++++++++++------ Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 21 +++++++------------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs index bdc7c7b1e2..a26fc585b1 100644 --- a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs +++ b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs @@ -8,6 +8,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Xtensive.Core @@ -125,7 +126,7 @@ public static void EnsureIndexIsValid(this IList list, int index) public static IReadOnlyList AsSafeWrapper(this List list) => list.AsReadOnly(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IReadOnlyList AsSafeWrapper(this IReadOnlyList list) => new ReadOnlyCollection(list); + public static IReadOnlyList AsSafeWrapper(this IReadOnlyList list) => Array.AsReadOnly(list.ToArray()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IReadOnlyList AsSafeWrapper(this T[] array) => Array.AsReadOnly(array); diff --git a/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs b/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs index e30f4cc7e5..2edd482f3a 100644 --- a/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs @@ -62,10 +62,8 @@ public TypeInfo AuxiliaryType /// Gets or sets ancestor association. /// /// The ancestor. - public NodeCollection Ancestors - { - get { return ancestors; } - } + public NodeCollection Ancestors => + ancestors ??= new NodeCollection(this, "Ancestors"); /// /// Gets the underlying index for this instance. @@ -195,6 +193,17 @@ public Tuple ExtractForeignKey(TypeInfo type, Tuple tuple) throw new InvalidOperationException(Strings.ExCanNotExtractForeignKey); } + public override void Lock(bool recursive) + { + base.Lock(recursive); + if (!recursive) + return; + if (ancestors is null || ancestors.Count == 0) + ancestors = NodeCollection.Empty; + else + ancestors.Lock(false); + } + /// public override void UpdateState() { @@ -245,7 +254,6 @@ public AssociationInfo(FieldInfo ownerField, TypeInfo targetType, Multiplicity m Multiplicity = multiplicity; OnOwnerRemove = onOwnerRemove; OnTargetRemove = onTargetRemove; - ancestors = new NodeCollection(this, "Ancestors"); } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index a1371b8302..2f65cc75b3 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -559,25 +559,22 @@ public AssociationInfo GetAssociation(TypeInfo targetType) a => a.TargetType.UnderlyingType.IsAssignableFrom(targetType.UnderlyingType)); } - public IReadOnlyList Associations => (IReadOnlyList)associations ?? Array.Empty(); + public IReadOnlyList Associations => associations; - public bool ContainsAssociation(string associationName) => associations?.Contains(associationName) == true; + public bool ContainsAssociation(string associationName) => associations.Contains(associationName); public void AddAssociation(AssociationInfo association) => - EnsureAssociations().Add(association); + associations.Add(association); public void AddAssociations(IReadOnlyList range) { if (range.Count > 0) { - EnsureAssociations().AddRange(range); + associations.AddRange(range); } } public void RemoveAssociation(AssociationInfo association) => - _ = EnsureAssociations().Remove(association); - - private NodeCollection EnsureAssociations() => - associations ??= new NodeCollection(this, "Associations"); + _ = associations.Remove(association); /// /// Gets or sets field's adapter index. @@ -678,8 +675,7 @@ public override void Lock(bool recursive) if (!recursive) return; Fields.Lock(true); - if (column != null) - column.Lock(true); + column?.Lock(true); if (Associations.Count > 1) { var sorted = associations.Reorder(); associations = new NodeCollection(associations.Owner, associations.Name); @@ -812,9 +808,8 @@ private FieldInfo(TypeInfo declaringType, TypeInfo reflectedType, FieldAttribute Attributes = attributes; this.declaringType = declaringType; this.reflectedType = reflectedType; - Fields = IsEntity || IsStructure - ? new FieldInfoCollection(this, "Fields") - : FieldInfoCollection.Empty; + Fields = IsEntity || IsStructure ? new FieldInfoCollection(this, "Fields") : FieldInfoCollection.Empty; + associations = IsEntity || IsEntitySet ? new NodeCollection(this, "Associations") : NodeCollection.Empty; } } } From 7610fdf9d043ea9a14c6105f2462f352f9e59901 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 11:40:30 -0700 Subject: [PATCH 3/8] AssociationInfo.AddAncestors() --- .../Orm/Building/Builders/AssociationBuilder.cs | 2 +- Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs | 4 ++-- Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs index e08a5538a0..472257d79e 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs @@ -80,7 +80,7 @@ public static void BuildReversedAssociation(BuildingContext context, Association AssociationInfo existing; if (!context.Model.Associations.TryGetValue(association.Name, out existing)) { context.Model.Associations.Add(association); - association.Ancestors.AddRange(field.Associations); + association.AddAncestors(field.Associations); var associationsToRemove = field.Associations .Where(a => a.TargetType == association.TargetType) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs index 4582d71ec4..aea1d5fdd2 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs @@ -239,7 +239,7 @@ private void PreprocessAssociations() .ToList(); if (pairedAssociations.Count > 0) { foreach (var paired in pairedAssociations) { - paired.First.Ancestors.AddRange(interfaceAssociations); + paired.First.AddAncestors(interfaceAssociations); if (paired.First.TargetType.IsInterface || typesWithProcessedInheritedAssociations.Contains(paired.First.TargetType)) AssociationBuilder.BuildReversedAssociation(context, paired.First, paired.Second); else { @@ -283,7 +283,7 @@ private void PreprocessAssociations() var interfaceAssociationsToRemove = interfaceAssociations .Where(ia => ia.OwnerType != association.OwnerType) .ToList(); - association.Ancestors.AddRange(interfaceAssociationsToRemove); + association.AddAncestors(interfaceAssociationsToRemove); foreach (var interfaceAssociation in interfaceAssociationsToRemove) interfaceAssociations.Remove(interfaceAssociation); } diff --git a/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs b/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs index 2edd482f3a..3d84508a72 100644 --- a/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs @@ -5,6 +5,7 @@ // Created: 2008.07.02 using System; +using System.Collections.Generic; using System.Linq; using Xtensive.Core; using Xtensive.Tuples.Transform; @@ -192,6 +193,13 @@ public Tuple ExtractForeignKey(TypeInfo type, Tuple tuple) throw new InvalidOperationException(Strings.ExCanNotExtractForeignKey); } + + internal void AddAncestors(IReadOnlyList associations) + { + if (associations.Count > 0) { + Ancestors.AddRange(associations); + } + } public override void Lock(bool recursive) { From 1af46c71151580b63a1c423acfe19f3390cdc13a Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 11:47:07 -0700 Subject: [PATCH 4/8] After-merge fix --- Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs | 3 +-- Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs index f546d45ae5..10c8f5ba1f 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs @@ -7,8 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection;>>>>>>> master -57 +using System.Reflection; using Xtensive.Core; using Xtensive.Orm.Building.Definitions; diff --git a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs index 5562a0fe41..cc88b53900 100644 --- a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs @@ -58,7 +58,7 @@ public override void Lock(bool recursive) throw Exceptions.NotInitialized("Fields"); fields = fields is List list ? list.AsReadOnly() - : (IList) fields.ToList().AsReadOnly(); + : fields.ToList().AsReadOnly(); base.Lock(recursive); } } From e47b26712af7f4a6aee2a64f93d53322a90ac926 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 11:53:24 -0700 Subject: [PATCH 5/8] Refactor PartialIndexFilterInfo.Lock() --- Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs index cc88b53900..7b65270871 100644 --- a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs @@ -56,9 +56,7 @@ public override void Lock(bool recursive) throw Exceptions.NotInitialized("Expression"); if (Fields==null) throw Exceptions.NotInitialized("Fields"); - fields = fields is List list - ? list.AsReadOnly() - : fields.ToList().AsReadOnly(); + fields = (fields as List ?? fields.ToList()).AsReadOnly(); base.Lock(recursive); } } From fdd4ae8649508407201b3c089e4f500c8dae2754 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 14:57:44 -0700 Subject: [PATCH 6/8] Fix Validators --- Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs | 2 +- Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs | 2 +- Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs | 2 +- Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs index 10c8f5ba1f..eeb9c8719d 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs @@ -53,7 +53,7 @@ public TypeInfo BuildType(TypeDef typeDef) MappingSchema = typeDef.MappingSchema, HasVersionRoots = TypeHelper.GetInterfacesUnordered(typeDef.UnderlyingType) .Any(static type => type == typeof(IHasVersionRoots)), - Validators = validators.AsReadOnly(), + Validators = validators.AsSafeWrapper(), }; if (typeDef.StaticTypeId != null) { diff --git a/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs b/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs index 79cf1c9b98..b7814e0c2a 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs @@ -287,7 +287,7 @@ public string PairTo /// /// Gets of instances associated with this field. /// - public List Validators { get; private set; } + public List Validators { get; } = new(); internal bool IsDeclaredAsNullable { diff --git a/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs b/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs index 090efb3157..071193139b 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs @@ -113,7 +113,7 @@ public NodeCollection Implementors /// /// Gets instances associated with this type. /// - public List Validators { get; private set; } + public List Validators { get; } = new(); /// /// Gets or sets the type discriminator value. diff --git a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs index 7b65270871..b37d3f18d9 100644 --- a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs @@ -56,7 +56,6 @@ public override void Lock(bool recursive) throw Exceptions.NotInitialized("Expression"); if (Fields==null) throw Exceptions.NotInitialized("Fields"); - fields = (fields as List ?? fields.ToList()).AsReadOnly(); base.Lock(recursive); } } From f5d24679f7eabb43175bca27ae353e092a08ba49 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 23:14:50 -0700 Subject: [PATCH 7/8] Revert Associations-related helpers --- .../Building/Builders/AssociationBuilder.cs | 10 +++---- .../Orm/Building/Builders/ModelBuilder.cs | 6 ++-- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 29 +++++++------------ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs index 472257d79e..bb8e4da57d 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs @@ -25,7 +25,7 @@ public static void BuildAssociation(BuildingContext context, FieldDef fieldDef, fieldDef.OnOwnerRemove, fieldDef.OnTargetRemove); association.Name = context.NameBuilder.BuildAssociationName(association); context.Model.Associations.Add(association); - field.AddAssociation(association); + field.Associations.Add(association); if (!fieldDef.PairTo.IsNullOrEmpty()) context.PairedAssociations.Add(new Pair(association, fieldDef.PairTo)); @@ -40,8 +40,8 @@ public static void BuildAssociation(BuildingContext context, AssociationInfo ori .Where(a => a.TargetType == association.TargetType) .ToList(); foreach (var toRemove in associationsToRemove) - field.RemoveAssociation(toRemove); - field.AddAssociation(association); + field.Associations.Remove(toRemove); + field.Associations.Add(association); var pairTo = context.PairedAssociations.Where(p => p.First==origin).FirstOrDefault(); if (pairTo.First!=null) @@ -86,9 +86,9 @@ public static void BuildReversedAssociation(BuildingContext context, Association .Where(a => a.TargetType == association.TargetType) .ToList(); foreach (var toRemove in associationsToRemove) - field.RemoveAssociation(toRemove); + field.Associations.Remove(toRemove); - field.AddAssociation(association); + field.Associations.Add(association); } } diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs index a9c1ad4f32..f5a78ddf82 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs @@ -282,7 +282,7 @@ bool associationFilter(AssociationInfo a) foreach (var association in associationsToRemove) { context.Model.Associations.Remove(association); - refField.RemoveAssociation(association); + refField.Associations.Remove(association); } foreach (var association in associationsToKeep) { var interfaceAssociationsToRemove = interfaceAssociations @@ -294,8 +294,8 @@ bool associationFilter(AssociationInfo a) } refField.AddAssociations(interfaceAssociations); foreach (var association in inheritedAssociations) { - if (!refField.ContainsAssociation(association.Name)) - refField.AddAssociation(association); + if (!refField.Associations.Contains(association.Name)) + refField.Associations.Add(association); if (!context.Model.Associations.Contains(association)) context.Model.Associations.Add(association); } diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index 2f65cc75b3..43e5d5c4ca 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -51,7 +51,6 @@ public sealed class FieldInfo : MappedNode, private TypeInfo declaringType; private FieldInfo parent; private ColumnInfo column; - private NodeCollection associations; private Type itemType; private string originalName; internal SegmentTransform valueExtractor; @@ -548,34 +547,26 @@ public AssociationInfo GetAssociation(TypeInfo targetType) case 0: return null; case 1: - return associations[0]; + return Associations[0]; } var ordered = IsLocked - ? associations - : associations.Reorder(); + ? Associations + : Associations.Reorder(); return ordered.FirstOrDefault( a => a.TargetType.UnderlyingType.IsAssignableFrom(targetType.UnderlyingType)); } - public IReadOnlyList Associations => associations; - - public bool ContainsAssociation(string associationName) => associations.Contains(associationName); - - public void AddAssociation(AssociationInfo association) => - associations.Add(association); + public NodeCollection Associations { get; private set; } public void AddAssociations(IReadOnlyList range) { if (range.Count > 0) { - associations.AddRange(range); + Associations.AddRange(range); } } - public void RemoveAssociation(AssociationInfo association) => - _ = associations.Remove(association); - /// /// Gets or sets field's adapter index. /// @@ -677,11 +668,11 @@ public override void Lock(bool recursive) Fields.Lock(true); column?.Lock(true); if (Associations.Count > 1) { - var sorted = associations.Reorder(); - associations = new NodeCollection(associations.Owner, associations.Name); - associations.AddRange(sorted); + var sorted = Associations.Reorder(); + Associations = new NodeCollection(Associations.Owner, Associations.Name); + Associations.AddRange(sorted); } - associations?.Lock(false); + Associations?.Lock(false); } private void CreateMappingInfo() @@ -809,7 +800,7 @@ private FieldInfo(TypeInfo declaringType, TypeInfo reflectedType, FieldAttribute this.declaringType = declaringType; this.reflectedType = reflectedType; Fields = IsEntity || IsStructure ? new FieldInfoCollection(this, "Fields") : FieldInfoCollection.Empty; - associations = IsEntity || IsEntitySet ? new NodeCollection(this, "Associations") : NodeCollection.Empty; + Associations = IsEntity || IsEntitySet ? new NodeCollection(this, "Associations") : NodeCollection.Empty; } } } From a62073ee24e56034099aa2bbb6642081141f2162 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 25 Jul 2023 23:23:58 -0700 Subject: [PATCH 8/8] Remove AddAssociations() --- .../Orm/Building/Builders/ModelBuilder.cs | 4 +++- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 11 +++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs index f5a78ddf82..571daa2606 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs @@ -292,7 +292,9 @@ bool associationFilter(AssociationInfo a) foreach (var interfaceAssociation in interfaceAssociationsToRemove) interfaceAssociations.Remove(interfaceAssociation); } - refField.AddAssociations(interfaceAssociations); + if (interfaceAssociations.Count > 0) { + refField.Associations.AddRange(interfaceAssociations); + } foreach (var association in inheritedAssociations) { if (!refField.Associations.Contains(association.Name)) refField.Associations.Add(association); diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index 43e5d5c4ca..1f8701ccc4 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -560,13 +560,6 @@ public AssociationInfo GetAssociation(TypeInfo targetType) public NodeCollection Associations { get; private set; } - public void AddAssociations(IReadOnlyList range) - { - if (range.Count > 0) { - Associations.AddRange(range); - } - } - /// /// Gets or sets field's adapter index. /// @@ -774,7 +767,9 @@ public FieldInfo Clone() DeclaringField = DeclaringField, Validators = Validators.Select(v => v.CreateNew()).ToArray(), }; - clone.AddAssociations(Associations); + if (Associations.Count > 0) { + clone.Associations.AddRange(Associations); + } return clone; }