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 cc257e8ef0..8f931b5f33 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.SortToList(nodes, out removedEdges); if (!allowLoops) - Assert.AreEqual(nodeCount, result.Count); + Assert.AreEqual(nodeCount, result.Count()); } GC.GetTotalMemory(true); } diff --git a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs index d50fdcb846..a26fc585b1 100644 --- a/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs +++ b/Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs @@ -8,7 +8,8 @@ using System; using System.Collections; using System.Collections.Generic; - +using System.Linq; +using System.Runtime.CompilerServices; namespace Xtensive.Core { @@ -119,5 +120,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) => Array.AsReadOnly(list.ToArray()); + + [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 3ec6657269..10b3400c27 100644 --- a/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs +++ b/Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs @@ -18,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); @@ -28,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; } /// @@ -38,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; @@ -50,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; @@ -60,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; } /// @@ -253,7 +253,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; @@ -263,7 +263,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 b9900b549c..c749422741 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; @@ -142,14 +143,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..bb8e4da57d 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 3c6050ea40..571daa2606 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 { @@ -288,11 +288,13 @@ bool associationFilter(AssociationInfo a) 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); } - refField.Associations.AddRange(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/Building/Builders/TypeBuilder.cs b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs index 0db467dc80..eeb9c8719d 100644 --- a/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs +++ b/Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; + using Xtensive.Core; using Xtensive.Orm.Building.Definitions; using Xtensive.Orm.Building.DependencyGraph; @@ -52,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) { @@ -234,6 +235,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, @@ -244,17 +255,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 e911e81c5c..a4835f2fea 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; } diff --git a/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs b/Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs index 3fb5d381fd..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 { @@ -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 48c4c8fd18..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. @@ -219,8 +219,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/AssociationInfo.cs b/Orm/Xtensive.Orm/Orm/Model/AssociationInfo.cs index e30f4cc7e5..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; @@ -62,10 +63,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. @@ -194,6 +193,24 @@ 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) + { + 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 +262,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/ColumnIndexMap.cs b/Orm/Xtensive.Orm/Orm/Model/ColumnIndexMap.cs index 9e49ed67c6..9d859e1db5 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 diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index b15369e0e1..1f8701ccc4 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; @@ -60,7 +59,6 @@ public sealed class FieldInfo : MappedNode, private int fieldId; private int? cachedHashCode; - private IList validators; private Segment mappingInfo; #region IsXxx properties @@ -545,24 +543,22 @@ 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 - : associations.Reorder(); + ? Associations + : Associations.Reorder(); return ordered.FirstOrDefault( a => a.TargetType.UnderlyingType.IsAssignableFrom(targetType.UnderlyingType)); } - public NodeCollection Associations - { - get { return associations; } - } + public NodeCollection Associations { get; private set; } /// /// Gets or sets field's adapter index. @@ -582,14 +578,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 +647,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 +658,14 @@ 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) { - var sorted = associations.Reorder(); - associations = new NodeCollection(associations.Owner, associations.Name); - associations.AddRange(sorted); + column?.Lock(true); + 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 +765,11 @@ 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); + if (Associations.Count > 0) { + clone.Associations.AddRange(Associations); + } return clone; } @@ -805,10 +794,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; - associations = new NodeCollection(this, "Associations"); + Fields = IsEntity || IsStructure ? new FieldInfoCollection(this, "Fields") : FieldInfoCollection.Empty; + Associations = IsEntity || IsEntitySet ? new NodeCollection(this, "Associations") : NodeCollection.Empty; } } -} \ 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 7ced941373..cf8643f398 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.AllDescendants.Count + 1) {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 370095e3be..23c5633102 100644 --- a/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/IndexInfo.cs @@ -35,7 +35,7 @@ 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 IReadOnlyList filterByTypes; @@ -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); @@ -393,7 +393,7 @@ private void CreateColumns() var result = new List(keyColumns.Count + valueColumns.Count); result.AddRange(keyColumns.Select(static 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 8de3e95f09..b37d3f18d9 100644 --- a/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/PartialIndexFilterInfo.cs @@ -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,9 +56,6 @@ public override void Lock(bool recursive) throw Exceptions.NotInitialized("Expression"); if (Fields==null) throw Exceptions.NotInitialized("Fields"); - fields = fields is List list - ? list.AsReadOnly() - : (IList) 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 cb3521f44e..f2b41d653f 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -610,7 +610,7 @@ public IEnumerable GetTargetAssociations() if (!IsLocked) { return result; } - targetAssociations = result.ToList().AsReadOnly(); + targetAssociations = result.ToList().AsSafeWrapper(); } return targetAssociations; } @@ -625,7 +625,7 @@ public IEnumerable GetOwnerAssociations() if (!IsLocked) { return result; } - ownerAssociations = result.ToList().AsReadOnly(); + ownerAssociations = result.ToList().AsSafeWrapper(); } return ownerAssociations; } @@ -737,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) @@ -798,7 +798,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; @@ -806,7 +806,7 @@ public override void UpdateState() sortedRemovalSequence.Capacity = sequenceSize; } sortedRemovalSequence.AddRange(sequence.Where(a => a.Ancestors.Count == 0)); - removalSequence = sortedRemovalSequence.AsReadOnly(); + removalSequence = sortedRemovalSequence.AsSafeWrapper(); } } 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 2ef5b920a5..36210481bf 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/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 cf161f4969..548d13ad2f 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 { diff --git a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs index 011c19f2bb..02362d6e7e 100644 --- a/Orm/Xtensive.Orm/Reflection/TypeHelper.cs +++ b/Orm/Xtensive.Orm/Reflection/TypeHelper.cs @@ -732,15 +732,15 @@ public static IReadOnlyList GetInterfacesUnordered(Type type) => /// 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. /// Interfaces will be ordered from the very base ones to ancestors. /// /// The type to get the interfaces of. - public static Type[] GetInterfacesOrderedByInheritance(this Type type) => - OrderedInterfaces.GetOrAdd(type, t => t.GetInterfaces().OrderByInheritance().ToArray()); + public static Type[] GetInterfacesOrderByInheritance(this Type type) => + 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