Skip to content

Commit b9648f4

Browse files
committed
Correct TypeInfo.cs
1 parent 5c3f1c7 commit b9648f4

2 files changed

Lines changed: 138 additions & 79 deletions

File tree

Orm/Xtensive.Orm/Orm/Building/Builders/StorageMappingValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private void EnsureIntefacesAreImplementedWithinSingleDatabase()
7373
if (implementors.Count == 0) {
7474
continue; // shouldn't reach here, but it's safer to do check anyway
7575
}
76-
var firstImplementor = implementors[0];
76+
var firstImplementor = implementors.First();
7777
foreach (var implementor in implementors.Skip(1))
7878
if (firstImplementor.MappingDatabase != implementor.MappingDatabase)
7979
throw new DomainBuilderException(string.Format(

Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs

Lines changed: 137 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
using System;
88
using System.Collections;
9-
using System.Collections.Generic;
109
using System.Collections.Immutable;
10+
using System.Collections.Generic;
1111
using System.Collections.ObjectModel;
1212
using System.Diagnostics;
1313
using System.Linq;
@@ -17,6 +17,7 @@
1717
using Xtensive.Tuples;
1818
using Xtensive.Tuples.Transform;
1919
using Tuple = Xtensive.Tuples.Tuple;
20+
using JetBrains.Annotations;
2021

2122
namespace Xtensive.Orm.Model
2223
{
@@ -39,7 +40,7 @@ public sealed class TypeInfo : SchemaMappedNode
3940
/// </summary>
4041
public const int MinTypeId = 100;
4142

42-
private static readonly IReadOnlySet<TypeInfo> EmptyTypes = ImmutableHashSet<TypeInfo>.Empty;
43+
private static readonly ImmutableHashSet<TypeInfo> EmptyTypes = ImmutableHashSet.Create<TypeInfo>();
4344

4445
private readonly ColumnInfoCollection columns;
4546
private readonly FieldMap fieldMap;
@@ -69,15 +70,45 @@ public sealed class TypeInfo : SchemaMappedNode
6970
private FieldInfo typeIdField;
7071

7172
private TypeInfo ancestor;
73+
74+
private IReadOnlySet<TypeInfo> ancestors;
75+
76+
private ISet<TypeInfo> directDescendants;
77+
private IReadOnlySet<TypeInfo> allDescendants;
78+
private ISet<TypeInfo> directInterfaces;
79+
private IReadOnlySet<TypeInfo> allInterfaces;
80+
private ISet<TypeInfo> directImplementors;
81+
private IReadOnlySet<TypeInfo> allImplementors;
82+
private IReadOnlySet<TypeInfo> typeWithAncestorsAndInterfaces;
83+
84+
#region Hierarchical structure properties
85+
86+
/// <summary>
87+
/// Gets the ancestor.
88+
/// </summary>
7289
public TypeInfo Ancestor {
73-
get => ancestor;
74-
internal set => ancestor = ancestor == null ? value : throw Exceptions.AlreadyInitialized(nameof(Ancestor));
90+
get { return ancestor; }
91+
internal set {
92+
if (ancestor != null)
93+
throw Exceptions.AlreadyInitialized(nameof(Ancestor));
94+
ancestor = value;
95+
}
7596
}
7697

98+
/// <summary>
99+
/// Gets the root of the hierarchy.
100+
/// </summary>
101+
[CanBeNull]
102+
public TypeInfo Root =>
103+
IsInterface || IsStructure
104+
? null
105+
: IsLocked
106+
? Hierarchy.Root
107+
: Ancestors.FirstOrDefault() ?? this;
108+
77109
/// <summary>
78110
/// Gets the ancestors recursively. Inheritor-to-root order.
79111
/// </summary>
80-
/// <returns>The ancestor</returns>
81112
public IEnumerable<TypeInfo> AncestorChain
82113
{
83114
get {
@@ -87,93 +118,103 @@ public IEnumerable<TypeInfo> AncestorChain
87118
}
88119
}
89120

90-
private IReadOnlyList<TypeInfo> ancestors;
91-
92121
/// <summary>
93-
/// Gets the ancestors recursively. Root-to-inheritor order. Reverse of AncestorChain.
122+
/// Gets the ancestors recursively. Root-to-inheritor order. Reverse of <see cref="AncestorChain"/>.
94123
/// </summary>
95-
/// <returns>The ancestor</returns>
96-
public IReadOnlyList<TypeInfo> Ancestors => ancestors ??= AncestorChain.Reverse().ToList();
124+
public IReadOnlySet<TypeInfo> Ancestors =>
125+
ancestors ??= new Collections.ReadOnlyHashSet<TypeInfo>(AncestorChain.Reverse().ToHashSet());
97126

98-
private HashSet<TypeInfo> descendants;
99-
public IReadOnlySet<TypeInfo> DirectDescendants => descendants ?? EmptyTypes;
127+
/// <summary>
128+
/// Gets direct descendants of this instance.
129+
/// </summary>
130+
public IReadOnlySet<TypeInfo> DirectDescendants =>
131+
(IReadOnlySet<TypeInfo>) directDescendants ?? EmptyTypes;
100132

101-
private IReadOnlySet<TypeInfo> recursiveDescendants;
133+
/// <summary>
134+
/// Gets all descendants (both direct and nested) of this instance.
135+
/// </summary>
102136
public IReadOnlySet<TypeInfo> AllDescendants
103137
{
104138
get {
105-
if (recursiveDescendants == null) {
139+
if (allDescendants == null) {
106140
if (DirectDescendants.Count == 0) {
107-
recursiveDescendants = DirectDescendants;
141+
allDescendants = DirectDescendants;
108142
}
109143
else {
110144
var set = new HashSet<TypeInfo>(DirectDescendants);
111145
set.UnionWith(DirectDescendants.SelectMany(static o => o.AllDescendants));
112-
recursiveDescendants = set;
146+
allDescendants = new Collections.ReadOnlyHashSet<TypeInfo>(set);
113147
}
114148
}
115-
return recursiveDescendants;
149+
return allDescendants;
116150
}
117151
}
118152

119-
private HashSet<TypeInfo> interfaces;
120-
public IReadOnlySet<TypeInfo> DirectInterfaces => interfaces ?? EmptyTypes;
121-
122-
private IReadOnlyList<TypeInfo> recursiveInterfaces;
123-
public IReadOnlyList<TypeInfo> AllInterfaces =>
124-
recursiveInterfaces ??= (IsInterface ? DirectInterfaces : DirectInterfaces.Concat(AncestorChain.SelectMany(static o => o.DirectInterfaces))).ToList();
153+
/// <summary>
154+
/// Gets the persistent interfaces this instance implements directly.
155+
/// </summary>
156+
public IReadOnlySet<TypeInfo> DirectInterfaces =>
157+
(IReadOnlySet<TypeInfo>) directInterfaces ?? EmptyTypes;
125158

126-
private HashSet<TypeInfo> implementors;
159+
/// <summary>
160+
/// Gets all the persistent interfaces (both direct and non-direct) this instance implements.
161+
/// </summary>
162+
public IReadOnlySet<TypeInfo> AllInterfaces =>
163+
allInterfaces ??= (IsInterface
164+
? DirectInterfaces
165+
: new Collections.ReadOnlyHashSet<TypeInfo>(DirectInterfaces.Concat(AncestorChain.SelectMany(static o => o.DirectInterfaces)).ToHashSet()));
127166

128167
/// <summary>
129168
/// Gets the direct implementors of this instance.
130169
/// </summary>
131-
public IReadOnlySet<TypeInfo> DirectImplementors => implementors ?? EmptyTypes;
170+
public IReadOnlySet<TypeInfo> DirectImplementors =>
171+
(IReadOnlySet<TypeInfo>) directImplementors ?? EmptyTypes;
132172

133-
private IReadOnlyList<TypeInfo> recursiveImplementors;
134-
public IReadOnlyList<TypeInfo> AllImplementors
173+
174+
/// <summary>
175+
/// Gets both direct and non-direct implementors of this instance.
176+
/// </summary>
177+
public IReadOnlySet<TypeInfo> AllImplementors
135178
{
136179
get {
137-
if (recursiveImplementors == null) {
180+
if (allImplementors == null) {
138181
if (DirectImplementors.Count == 0) {
139-
recursiveImplementors = Array.Empty<TypeInfo>();
182+
allImplementors = EmptyTypes;
140183
}
141184
else {
142-
var list = new List<TypeInfo>(DirectImplementors.Count);
185+
var allSet = new HashSet<TypeInfo>(DirectImplementors.Count);
143186
foreach (var item in DirectImplementors) {
144-
list.Add(item);
187+
_ = allSet.Add(item);
145188
if (!item.IsInterface) {
146-
list.AddRange(item.AllDescendants);
189+
foreach (var descendant in item.AllDescendants)
190+
_ = allSet.Add(descendant);
147191
}
148192
}
149-
recursiveImplementors = list;
193+
allImplementors = new Collections.ReadOnlyHashSet<TypeInfo>(allSet);
150194
}
151195
}
152-
return recursiveImplementors;
196+
return allImplementors;
153197
}
154198
}
155199

156200
/// <summary>
157-
/// Gets the ancestors recursively. Root-to-inheritor order.
201+
/// Gets all ancestors, all interfaces with this instacne included.
158202
/// </summary>
159-
/// <returns>The ancestor</returns>
160-
[Obsolete("Use Ancestors property instead")]
161-
public IReadOnlyList<TypeInfo> GetAncestors() => Ancestors;
162-
163-
private IReadOnlySet<TypeInfo> typeWithAncestorsAndInterfaces;
164-
public IReadOnlySet<TypeInfo> TypeWithAncestorsAndInterfaces
203+
internal IReadOnlySet<TypeInfo> TypeWithAncestorsAndInterfaces
165204
{
166205
get {
167206
if (typeWithAncestorsAndInterfaces == null) {
168207
var candidates = new HashSet<TypeInfo>(Ancestors);
169208
candidates.UnionWith(AllInterfaces);
170-
candidates.Add(this);
209+
_ = candidates.Add(this);
171210
typeWithAncestorsAndInterfaces = candidates;
172211
}
173212
return typeWithAncestorsAndInterfaces;
174213
}
175214
}
176215

216+
#endregion
217+
177218
#region IsXxx properties
178219

179220
/// <summary>
@@ -232,7 +273,7 @@ public bool IsSystem
232273

233274
/// <summary>
234275
/// Gets a value indicating whether this instance is a leaf type,
235-
/// i.e. its <see cref="GetDescendants()"/> method returns <see langword="0" />.
276+
/// i.e. its <see cref="DirectDescendants"/> method returns empty collection.
236277
/// </summary>
237278
public bool IsLeaf
238279
{
@@ -503,28 +544,6 @@ internal set {
503544

504545
internal FieldAccessorProvider Accessors { get; private set; }
505546

506-
/// <summary>
507-
/// Gets the direct implementors of this instance.
508-
/// </summary>
509-
/// <param name="recursive">if set to <see langword="true"/> then both direct and non-direct implementors will be returned.</param>
510-
[Obsolete("Use Implementors/RecursiveImplementors properties instead")]
511-
public IEnumerable<TypeInfo> GetImplementors(bool recursive = false) => recursive ? AllImplementors : DirectImplementors;
512-
513-
/// <summary>
514-
/// Gets the persistent interfaces this instance implements.
515-
/// </summary>
516-
/// <param name="recursive">if set to <see langword="true"/> then both direct and non-direct implemented interfaces will be returned.</param>
517-
[Obsolete("Use Interfaces/RecursiveInterfaces properties instead")]
518-
public IEnumerable<TypeInfo> GetInterfaces(bool recursive = false) => recursive ? AllInterfaces : DirectInterfaces;
519-
520-
/// <summary>
521-
/// Gets descendants of this instance.
522-
/// </summary>
523-
/// <param name="recursive">if set to <see langword="true"/> then both direct and nested descendants will be returned.</param>
524-
/// <returns></returns>
525-
[Obsolete("Use Descendants/RecursiveDescendants properties instead")]
526-
public IEnumerable<TypeInfo> GetDescendants(bool recursive) => recursive ? AllDescendants : DirectDescendants;
527-
528547
/// <summary>
529548
/// Creates the tuple prototype with specified <paramref name="primaryKey"/>.
530549
/// </summary>
@@ -555,13 +574,42 @@ public Tuple InjectPrimaryKey(Tuple entityTuple, Tuple primaryKey)
555574
return primaryKeyInjector.Apply(TupleTransformType.Tuple, primaryKey, entityTuple);
556575
}
557576

577+
/// <summary>
578+
/// Gets the direct implementors of this instance.
579+
/// </summary>
580+
/// <param name="recursive">if set to <see langword="true"/> then both direct and non-direct implementors will be returned.</param>
581+
[Obsolete("Use DirectImplementors/AllImplementors properties instead")]
582+
public IEnumerable<TypeInfo> GetImplementors(bool recursive = false) => recursive ? AllImplementors : DirectImplementors;
558583

584+
/// <summary>
585+
/// Gets the persistent interfaces this instance implements.
586+
/// </summary>
587+
/// <param name="recursive">if set to <see langword="true"/> then both direct and non-direct implemented interfaces will be returned.</param>
588+
[Obsolete("Use DirectInterfaces/AllInterfaces properties instead")]
589+
public IEnumerable<TypeInfo> GetInterfaces(bool recursive = false) => recursive ? AllInterfaces : DirectInterfaces;
590+
591+
/// <summary>
592+
/// Gets descendants of this instance.
593+
/// </summary>
594+
/// <param name="recursive">if set to <see langword="true"/> then both direct and nested descendants will be returned.</param>
595+
/// <returns></returns>
596+
[Obsolete("Use DirectDescendants/AllDescendants properties instead")]
597+
public IEnumerable<TypeInfo> GetDescendants(bool recursive) => recursive ? AllDescendants : DirectDescendants;
598+
599+
/// <summary>
600+
/// Gets the ancestors recursively. Root-to-inheritor order.
601+
/// </summary>
602+
/// <returns>The ancestor</returns>
603+
[Obsolete("Use Ancestors property instead")]
604+
public IReadOnlyList<TypeInfo> GetAncestors() => Ancestors.ToList();
605+
606+
/// <summary>
607+
/// Gets the root of the hierarchy.
559608
/// </summary>
560609
/// <returns>The hierarchy root.</returns>
561-
public TypeInfo GetRoot() =>
562-
IsInterface || IsStructure
563-
? null
564-
: (Ancestors.FirstOrDefault() ?? this);
610+
[Obsolete("Use Root property instead")]
611+
[CanBeNull]
612+
public TypeInfo GetRoot() => Root;
565613

566614
public IEnumerable<AssociationInfo> GetTargetAssociations()
567615
{
@@ -789,6 +837,16 @@ public override void Lock(bool recursive)
789837

790838
validators = Array.AsReadOnly(validators.ToArray());
791839

840+
directDescendants = directDescendants != null
841+
? new Collections.ReadOnlyHashSet<TypeInfo>((HashSet<TypeInfo>) directDescendants)
842+
: EmptyTypes;
843+
directInterfaces = directInterfaces != null
844+
? new Collections.ReadOnlyHashSet<TypeInfo>((HashSet<TypeInfo>) directInterfaces)
845+
: EmptyTypes;
846+
directImplementors = directImplementors!=null
847+
? new Collections.ReadOnlyHashSet<TypeInfo>((HashSet<TypeInfo>) directImplementors)
848+
: EmptyTypes;
849+
792850
affectedIndexes.Lock(true);
793851
indexes.Lock(true);
794852
columns.Lock(true);
@@ -798,6 +856,16 @@ public override void Lock(bool recursive)
798856

799857
#region Private / internal methods
800858

859+
860+
internal void AddDescendant(TypeInfo descendant) =>
861+
(directDescendants ??= new HashSet<TypeInfo>()).Add(descendant);
862+
863+
internal void AddInterface(TypeInfo iface) =>
864+
(directInterfaces ??= new HashSet<TypeInfo>()).Add(iface);
865+
866+
internal void AddImplementor(TypeInfo implementor) =>
867+
(directImplementors ??= new HashSet<TypeInfo>()).Add(implementor);
868+
801869
private KeyInfo GetKey() =>
802870
Hierarchy != null ? Hierarchy.Key
803871
: IsInterface ? DirectImplementors.First().Hierarchy.Key
@@ -902,15 +970,6 @@ public override string ToString()
902970
return Name;
903971
}
904972

905-
internal void AddDescendant(TypeInfo descendant) =>
906-
(descendants ??= new()).Add(descendant);
907-
908-
internal void AddInterface(TypeInfo iface) =>
909-
(interfaces ??= new()).Add(iface);
910-
911-
internal void AddImplementor(TypeInfo implementor) =>
912-
(implementors ??= new()).Add(implementor);
913-
914973
// Constructors
915974

916975
/// <summary>

0 commit comments

Comments
 (0)