diff --git a/Directory.Build.props b/Directory.Build.props index e1e2bf5fbb..3028fe456b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -132,5 +132,6 @@ $(DefineConstants);DO_DATEONLY + $(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..3c72faf921 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,29 @@ public static void EnsureIndexIsValid(this IList list, int index) if (index < 0 || index >= list.Count) throw new IndexOutOfRangeException(Strings.ExIndexOutOfRange); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this List list) => +#if DO_SAFE_COLLECTION_WRAPPER + list.AsReadOnly(); +#else + list; +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this IReadOnlyList list) => +#if DO_SAFE_COLLECTION_WRAPPER + new ReadOnlyCollection(list); +#else + list; +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyList AsSafeWrapper(this T[] array) => +#if DO_SAFE_COLLECTION_WRAPPER + Array.AsReadOnly(array); +#else + 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 8ec6e63efc..a37cd78371 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs @@ -533,7 +533,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 @@ -760,7 +760,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 be674702f7..3e1ef0c2c7 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 299bf7c8fe..1a27bc1470 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 ba291e6e7f..aa9b205e6e 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs @@ -90,9 +90,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 a2686c6f49..ffb293a0d3 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.RecursiveDescendants); - Types = list.AsReadOnly(); + Types = list.AsSafeWrapper(); if (Types.Count == 1) InheritanceSchema = InheritanceSchema.ConcreteTable; if (TypeDiscriminatorMap != null) 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 ca2270fc6d..a166ed7e17 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(); } } @@ -84,8 +85,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) @@ -126,7 +127,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 a7e0f8abd1..c5d0e9692e 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -52,7 +52,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; @@ -504,14 +503,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). @@ -565,7 +557,7 @@ public IEnumerable GetTargetAssociations() if (!IsLocked) { return result; } - targetAssociations = result.ToList().AsReadOnly(); + targetAssociations = result.ToList().AsSafeWrapper(); } return targetAssociations; } @@ -580,7 +572,7 @@ public IEnumerable GetOwnerAssociations() if (!IsLocked) { return result; } - ownerAssociations = result.ToList().AsReadOnly(); + ownerAssociations = result.ToList().AsSafeWrapper(); } return ownerAssociations; } @@ -694,7 +686,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) @@ -753,7 +745,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; @@ -761,7 +753,7 @@ public override void UpdateState() sortedRemovalSequence.Capacity = sequenceSize; } sortedRemovalSequence.AddRange(sequence.Where(a => a.Ancestors.Count == 0)); - removalSequence = sortedRemovalSequence.AsReadOnly(); + removalSequence = sortedRemovalSequence.AsSafeWrapper(); } } @@ -782,8 +774,6 @@ public override void Lock(bool recursive) if (!recursive) return; - validators = Array.AsReadOnly(validators.ToArray()); - affectedIndexes.Lock(true); indexes.Lock(true); columns.Lock(true); 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 9f9c3f0abf..fdac0b9cb8 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 e6af744980..b2234207e0 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Index.cs @@ -224,7 +224,7 @@ private QueryAndBindings BuildFilteredQuery(IndexInfo index) SqlExpression filter = null; var type = index.ReflectedType; var discriminatorMap = type.Hierarchy.TypeDiscriminatorMap; - var filterByTypes = index.FilterByTypes.ToList(); + var filterByTypes = index.FilterByTypes; var filterByTypesCount = filterByTypes.Count; if (underlyingIndex.IsTyped && discriminatorMap != null) { var columnType = discriminatorMap.Column.ValueType; 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 b6a882c8f4..0818aa3698 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. @@ -166,18 +165,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