Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,7 @@
</ItemGroup>

<Import Condition="Exists('User.Directory.Build.props')" Project="User.Directory.Build.props" />
<PropertyGroup>
<DefineConstants Condition="'$(DO_SAFE_COLLECTION_WRAPPER)'!='false'">$(DefineConstants);DO_SAFE_COLLECTION_WRAPPER</DefineConstants>
</PropertyGroup>
</Project>
4 changes: 2 additions & 2 deletions Orm/Xtensive.Orm.Tests.Core/Helpers/TopologicalSorterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static void InternalPerformanceTest(int nodeCount, int averageConnection
List<Node<int, int>> removedEdges;
var result = TopologicalSorter.Sort(nodes, out removedEdges);
if (!allowLoops)
Assert.AreEqual(nodeCount, result.Count);
Assert.AreEqual(nodeCount, result.Count());
}
GC.GetTotalMemory(true);
}
Expand Down Expand Up @@ -132,7 +132,7 @@ public void CombinedTest()
private void TestSort<T>(T[] data, Predicate<T, T> connector, T[] expected, T[] loops)
{
List<Node<T, object>> actualLoopNodes;
List<T> actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes);
List<T> actual = TopologicalSorter.Sort(data, connector, out actualLoopNodes).ToList();
T[] actualLoops = null;
if (actualLoopNodes != null)
actualLoops = actualLoopNodes
Expand Down
24 changes: 22 additions & 2 deletions Orm/Xtensive.Orm/Core/Extensions/ListExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System;
using System.Collections;
using System.Collections.Generic;

using System.Runtime.CompilerServices;

namespace Xtensive.Core
{
Expand Down Expand Up @@ -119,5 +119,25 @@ public static void EnsureIndexIsValid(this IList list, int index)
if (index < 0 || index >= list.Count)
throw new IndexOutOfRangeException(Strings.ExIndexOutOfRange);
}

#if DO_SAFE_COLLECTION_WRAPPER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this List<T> list) => list.AsReadOnly();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this IReadOnlyList<T> list) => new ReadOnlyCollection(list);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this T[] array) => Array.AsReadOnly(array);
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this List<T> list) => list;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this IReadOnlyList<T> list) => list;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyList<T> AsSafeWrapper<T>(this T[] array) => array;
#endif
}
}
}
17 changes: 9 additions & 8 deletions Orm/Xtensive.Orm/Linq/ExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using Xtensive.Core;

namespace Xtensive.Linq
{
Expand All @@ -17,7 +18,7 @@ namespace Xtensive.Linq
/// </summary>
public abstract class ExpressionVisitor : ExpressionVisitor<Expression>
{
protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
protected override IReadOnlyList<Expression> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
{
bool isChanged = false;
var results = new List<Expression>(expressions.Count);
Expand All @@ -27,7 +28,7 @@ protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCo
results.Add(p);
isChanged |= !ReferenceEquals(expression, p);
}
return isChanged ? results.AsReadOnly() : expressions;
return isChanged ? results.AsSafeWrapper() : expressions;
}

/// <summary>
Expand All @@ -37,8 +38,8 @@ protected override ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCo
/// <returns>Visit result.</returns>
protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
{
ReadOnlyCollection<Expression> 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;
Expand All @@ -49,7 +50,7 @@ protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
/// </summary>
/// <param name="original">The original element initializer list.</param>
/// <returns>Visit result.</returns>
protected virtual ReadOnlyCollection<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
protected virtual IReadOnlyList<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
{
var results = new List<ElementInit>();
bool isChanged = false;
Expand All @@ -59,7 +60,7 @@ protected virtual ReadOnlyCollection<ElementInit> VisitElementInitializerList(Re
results.Add(p);
isChanged |= !ReferenceEquals(originalIntializer, p);
}
return isChanged ? results.AsReadOnly() : original;
return isChanged ? results.AsSafeWrapper() : original;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -246,7 +247,7 @@ protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBindi
/// </summary>
/// <param name="original">The original binding list.</param>
/// <returns>Visit result.</returns>
protected virtual ReadOnlyCollection<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
protected virtual IReadOnlyList<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
{
var results = new List<MemberBinding>();
bool isChanged = false;
Expand All @@ -256,7 +257,7 @@ protected virtual ReadOnlyCollection<MemberBinding> 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)
Expand Down
5 changes: 3 additions & 2 deletions Orm/Xtensive.Orm/Linq/ExpressionVisitor{TResult}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using Xtensive.Reflection;
using Xtensive.Core;



Expand Down Expand Up @@ -139,14 +140,14 @@ protected virtual TResult Visit(Expression e)
/// </summary>
/// <param name="expressions">The expression list.</param>
/// <returns>Visit result.</returns>
protected virtual ReadOnlyCollection<TResult> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
protected virtual IReadOnlyList<TResult> VisitExpressionList(ReadOnlyCollection<Expression> expressions)
{
var results = new List<TResult>(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();
}

/// <summary>
Expand Down
10 changes: 5 additions & 5 deletions Orm/Xtensive.Orm/Orm/Building/Builders/AssociationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssociationInfo, string>(association, fieldDef.PairTo));
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions Orm/Xtensive.Orm/Orm/Building/Builders/IndexBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ private IndexInfo BuildFilterIndex(TypeInfo reflectedType, IndexInfo indexToFilt
& (IndexAttributes.Primary | IndexAttributes.Secondary | IndexAttributes.Unique | IndexAttributes.Abstract)
| IndexAttributes.Filtered | IndexAttributes.Virtual;
var result = new IndexInfo(reflectedType, attributes, indexToFilter, Array.Empty<IndexInfo>()) {
FilterByTypes = filterByTypes.ToList().AsReadOnly()
FilterByTypes = filterByTypes.ToList().AsSafeWrapper()
};

// Adding key columns
Expand Down Expand Up @@ -778,7 +778,7 @@ private IndexInfo BuildViewIndex(TypeInfo reflectedType, IndexInfo indexToApplyV
}

result.ValueColumns.AddRange(valueColumns);
result.SelectColumns = columnMap.AsReadOnly();
result.SelectColumns = columnMap.AsSafeWrapper();
result.Name = nameBuilder.BuildIndexName(reflectedType, result);
result.Group = BuildColumnGroup(result);

Expand Down
8 changes: 4 additions & 4 deletions Orm/Xtensive.Orm/Orm/Building/Builders/ModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
Expand Down
31 changes: 17 additions & 14 deletions Orm/Xtensive.Orm/Orm/Building/Builders/TypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,21 @@ 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,
MappingName = typeDef.MappingName,
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;
}
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions Orm/Xtensive.Orm/Orm/Building/BuildingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ public sealed class BuildingContext
/// <summary>
/// Gets all available <see cref="IModule"/> implementations.
/// </summary>
public ICollection<IModule> Modules { get; private set; }
public IReadOnlyList<IModule> Modules { get; }

/// <summary>
/// Gets all available <see cref="IModule2"/> implementations.
/// </summary>
public ICollection<IModule2> Modules2 { get; private set; }
public IReadOnlyList<IModule2> Modules2 { get; }

internal ModelDefBuilder ModelDefBuilder { get; set; }

Expand All @@ -85,8 +85,8 @@ internal BuildingContext(DomainBuilderConfiguration builderConfiguration)
ModelInspectionResult = new ModelInspectionResult();
DependencyGraph = new Graph<TypeDef>();

Modules = BuilderConfiguration.Services.Modules.ToList().AsReadOnly();
Modules2 = Modules.OfType<IModule2>().ToList().AsReadOnly();
Modules = BuilderConfiguration.Services.Modules.AsSafeWrapper();
Modules2 = Modules.OfType<IModule2>().ToList().AsSafeWrapper();
Validator = new Validator(builderConfiguration.Services.ProviderInfo.SupportedTypes);
DefaultSchemaInfo = builderConfiguration.DefaultSchemaInfo;
}
Expand Down
3 changes: 1 addition & 2 deletions Orm/Xtensive.Orm/Orm/Building/Definitions/FieldDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public string PairTo
/// <summary>
/// Gets of <see cref="IPropertyValidator"/> instances associated with this field.
/// </summary>
public IList<IPropertyValidator> Validators { get; private set; }
public List<IPropertyValidator> Validators { get; } = new();

internal bool IsDeclaredAsNullable
{
Expand Down Expand Up @@ -325,7 +325,6 @@ internal FieldDef(Type valueType, Validator validator)
if ((valueType.IsClass || valueType.IsInterface) && !IsStructure)
attributes |= FieldAttributes.Nullable;
ValueType = valueType;
Validators = new List<IPropertyValidator>();

// Nullable<T>
if (valueType.IsNullable()) {
Expand Down
4 changes: 1 addition & 3 deletions Orm/Xtensive.Orm/Orm/Building/Definitions/TypeDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public NodeCollection<TypeDef> Implementors
/// <summary>
/// Gets <see cref="IObjectValidator"/> instances associated with this type.
/// </summary>
public IList<IObjectValidator> Validators { get; private set; }
public List<IObjectValidator> Validators { get; } = new();

/// <summary>
/// Gets or sets the type discriminator value.
Expand Down Expand Up @@ -225,8 +225,6 @@ internal TypeDef(ModelDefBuilder builder, Type type, Validator validator)
implementors = IsInterface
? new NodeCollection<TypeDef>(this, "Implementors")
: NodeCollection<TypeDef>.Empty;

Validators = new List<IObjectValidator>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ namespace Xtensive.Orm.Internals.Prefetch
{
internal interface IHasNestedNodes
{
ReadOnlyCollection<BaseFieldNode> NestedNodes { get; }
IReadOnlyList<BaseFieldNode> NestedNodes { get; }

IReadOnlyCollection<Key> ExtractKeys(object target);

IHasNestedNodes ReplaceNestedNodes(ReadOnlyCollection<BaseFieldNode> nestedNodes);
IHasNestedNodes ReplaceNestedNodes(IReadOnlyList<BaseFieldNode> nestedNodes);
}
}
Loading