From 352a90e09749fb92de716c07afd68280e03f3e48 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sun, 12 Nov 2023 23:35:24 -0800 Subject: [PATCH 01/24] ARCH-6354: Optimize Get/SetFieldValue() to use integer indexes instead of search by string key --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 18 ++++++++++++++ Orm/Xtensive.Orm/Orm/Persistent.cs | 6 +++++ .../Stages/ImportReferencesStage.cs | 5 ++-- .../Stages/ModifyPersistentTypesStage.cs | 24 +++++++++++++++++-- .../Tasks/ImplementFieldAccessorTask.cs | 10 ++++---- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 5f73e413c5..1b5b842288 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -421,6 +421,24 @@ public FieldInfoCollection Fields get { return fields; } } + private FieldInfo[] persistentFields; + + internal FieldInfo[] PersistentFields + { + get { + if (persistentFields == null) { + var baseFields = Ancestor?.PersistentFields + ?? (IsEntity ? new[] { Fields[nameof(Entity.TypeId)] } : Array.Empty()); + + persistentFields = baseFields.Concat( + Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null) + .Except(baseFields).OrderBy(p => p.UnderlyingProperty.MetadataToken) + ).ToArray(); + } + return persistentFields; + } + } + /// /// Gets the field map for implemented interfaces. /// diff --git a/Orm/Xtensive.Orm/Orm/Persistent.cs b/Orm/Xtensive.Orm/Orm/Persistent.cs index 3e48bca07f..c63cb496bf 100644 --- a/Orm/Xtensive.Orm/Orm/Persistent.cs +++ b/Orm/Xtensive.Orm/Orm/Persistent.cs @@ -165,6 +165,9 @@ protected internal object GetFieldValue(string fieldName) return GetFieldValue(TypeInfo.Fields[fieldName]); } + protected internal T GetFieldValue(int fieldIndex) => + GetFieldValue(TypeInfo.PersistentFields[fieldIndex]); + /// /// Gets the field value. /// Field value type must be specified precisely. @@ -356,6 +359,9 @@ protected internal void SetFieldValue(string fieldName, object value) SetFieldValue(TypeInfo.Fields[fieldName], value); } + protected internal void SetFieldValue(int fieldIndex, T value) => + SetFieldValue(TypeInfo.PersistentFields[fieldIndex], value); + /// /// Sets the field value. /// Field value type must be specified precisely. diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs index c2f7b1b611..4102b030d8 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs @@ -25,6 +25,7 @@ public override ActionResult Execute(ProcessorContext context) } registry.OrmAssembly = ormAssembly; + var intType = context.TargetModule.TypeSystem.Int32; var stringType = context.TargetModule.TypeSystem.String; var voidType = context.TargetModule.TypeSystem.Void; @@ -59,13 +60,13 @@ public override ActionResult Execute(ProcessorContext context) var persistentGetter = new MethodReference("GetFieldValue", voidType, persistentType) {HasThis = true}; var getterType = new GenericParameter("!!T", persistentGetter); persistentGetter.ReturnType = getterType; - persistentGetter.Parameters.Add(new ParameterDefinition(stringType)); + persistentGetter.Parameters.Add(new ParameterDefinition(intType)); persistentGetter.GenericParameters.Add(getterType); registry.PersistentGetterDefinition = context.TargetModule.ImportReference(persistentGetter); var persistentSetter = new MethodReference("SetFieldValue", voidType, persistentType) {HasThis = true}; var setterType = new GenericParameter("!!T", persistentSetter); - persistentSetter.Parameters.Add(new ParameterDefinition(stringType)); + persistentSetter.Parameters.Add(new ParameterDefinition(intType)); persistentSetter.Parameters.Add(new ParameterDefinition(setterType)); persistentSetter.GenericParameters.Add(setterType); registry.PersistentSetterDefinition = context.TargetModule.ImportReference(persistentSetter); diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index 5f8e0c016b..97724a9e07 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -4,6 +4,8 @@ // Created by: Denis Krjuchkov // Created: 2013.08.21 +using System; +using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Xtensive.Orm.Weaver.Tasks; @@ -107,12 +109,29 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor)); } + private static int NumberOfPersistentPropertis(TypeInfo type) => + type is null + ? 0 + : type.Properties.Values.Where(p => p.IsPersistent).Count() + NumberOfPersistentPropertis(type.BaseType); + private void ProcessFields(ProcessorContext context, TypeInfo type) { + int propsInBaseClass = NumberOfPersistentPropertis(type.BaseType); + if (type.Kind == PersistentTypeKind.Entity) { + ++propsInBaseClass; // for TypeId + } + + var propertyToIndex = type.Properties.Values + .Where(p => p.IsPersistent) + .OrderBy(p => p.Definition.MetadataToken.ToInt32()) + .Select((p, idx) => KeyValuePair.Create(p, propsInBaseClass + idx)) + .ToDictionary(kv => kv.Key, kv => kv.Value); + foreach (var property in type.Properties.Values.Where(p => p.IsPersistent)) { if (!propertyChecker.ShouldProcess(property, context)) continue; + var persistentIndex = propertyToIndex[property]; var typeDefinition = type.Definition; var propertyDefinition = property.Definition; var persistentName = property.PersistentName ?? property.Name; @@ -120,16 +139,17 @@ private void ProcessFields(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new RemoveBackingFieldTask(typeDefinition, propertyDefinition)); // Getter context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Getter, - typeDefinition, propertyDefinition, persistentName)); + typeDefinition, propertyDefinition, persistentIndex)); // Setter if (property.IsKey) context.WeavingTasks.Add(new ImplementKeySetterTask(typeDefinition, propertyDefinition)); else context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Setter, - typeDefinition, propertyDefinition, persistentName)); + typeDefinition, propertyDefinition, persistentIndex)); if (property.PersistentName!=null) context.WeavingTasks.Add(new AddAttributeTask(propertyDefinition, context.References.OverrideFieldNameAttributeConstructor, property.PersistentName)); + ++persistentIndex; } } diff --git a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs index 508d120a20..533275e5be 100644 --- a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs +++ b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs @@ -15,7 +15,7 @@ internal sealed class ImplementFieldAccessorTask : WeavingTask { private readonly TypeDefinition type; private readonly PropertyDefinition property; - private readonly string persistentName; + private readonly int persistentIndex; private readonly AccessorKind kind; public override ActionResult Execute(ProcessorContext context) @@ -42,7 +42,7 @@ private void ImplementSetter(ProcessorContext context) body.Instructions.Clear(); var il = body.GetILProcessor(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, persistentName); + il.Emit(OpCodes.Ldc_I4, persistentIndex); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Tail); il.Emit(OpCodes.Call, accessor); @@ -57,7 +57,7 @@ private void ImplementGetter(ProcessorContext context) body.Instructions.Clear(); var il = body.GetILProcessor(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, persistentName); + il.Emit(OpCodes.Ldc_I4, persistentIndex); il.Emit(OpCodes.Tail); il.Emit(OpCodes.Call, accessor); il.Emit(OpCodes.Ret); @@ -77,14 +77,14 @@ private MethodReference GetAccessor(ProcessorContext context, return result; } - public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, string persistentName) + public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, int persistentIndex) { ArgumentNullException.ThrowIfNull(type); ArgumentNullException.ThrowIfNull(property); this.kind = kind; this.type = type; this.property = property; - this.persistentName = persistentName; + this.persistentIndex = persistentIndex; } } } \ No newline at end of file From 2f786ef51315cd9df941dfeb72e5cebfb03a0127 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:01:25 -0800 Subject: [PATCH 02/24] Correct for IssueJira0538 test case --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 1b5b842288..2bc73806a1 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -12,6 +12,7 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using System.Reflection; using System.Threading; using JetBrains.Annotations; using Xtensive.Core; @@ -421,6 +422,17 @@ public FieldInfoCollection Fields get { return fields; } } + private FieldInfo[] GetBaseFields(Type type, IEnumerable fields) + { + if (type == typeof(Entity)) { + return new[] { Fields[nameof(Entity.TypeId)] }; + } + var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); + var localFields = fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) + .OrderBy(p => p.UnderlyingProperty.MetadataToken); + return GetBaseFields(type.BaseType, fields).Concat(localFields).ToArray(); + } + private FieldInfo[] persistentFields; internal FieldInfo[] PersistentFields @@ -428,7 +440,9 @@ internal FieldInfo[] PersistentFields get { if (persistentFields == null) { var baseFields = Ancestor?.PersistentFields - ?? (IsEntity ? new[] { Fields[nameof(Entity.TypeId)] } : Array.Empty()); + ?? (IsEntity + ? GetBaseFields(UnderlyingType.BaseType, Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null)) + : Array.Empty()); persistentFields = baseFields.Concat( Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null) From b8e376fcb29b999f59cc702354b2419513a546f3 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:04:23 -0800 Subject: [PATCH 03/24] Refactor --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 2bc73806a1..7bf518713e 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -422,15 +422,17 @@ public FieldInfoCollection Fields get { return fields; } } - private FieldInfo[] GetBaseFields(Type type, IEnumerable fields) + private IEnumerable GetBaseFields(Type type, IEnumerable fields) { if (type == typeof(Entity)) { return new[] { Fields[nameof(Entity.TypeId)] }; } var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); - var localFields = fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) - .OrderBy(p => p.UnderlyingProperty.MetadataToken); - return GetBaseFields(type.BaseType, fields).Concat(localFields).ToArray(); + return GetBaseFields(type.BaseType, fields) + .Concat( + fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) + .OrderBy(p => p.UnderlyingProperty.MetadataToken) + ); } private FieldInfo[] persistentFields; @@ -441,7 +443,7 @@ internal FieldInfo[] PersistentFields if (persistentFields == null) { var baseFields = Ancestor?.PersistentFields ?? (IsEntity - ? GetBaseFields(UnderlyingType.BaseType, Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null)) + ? GetBaseFields(UnderlyingType.BaseType, Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null)).ToArray() : Array.Empty()); persistentFields = baseFields.Concat( From 38d530654fb1e11b21be594f259fe29a178a06b4 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:09:38 -0800 Subject: [PATCH 04/24] Refactor --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 59 ++++++++++++-------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 7bf518713e..7ac961eafa 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -422,38 +422,8 @@ public FieldInfoCollection Fields get { return fields; } } - private IEnumerable GetBaseFields(Type type, IEnumerable fields) - { - if (type == typeof(Entity)) { - return new[] { Fields[nameof(Entity.TypeId)] }; - } - var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); - return GetBaseFields(type.BaseType, fields) - .Concat( - fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) - .OrderBy(p => p.UnderlyingProperty.MetadataToken) - ); - } - private FieldInfo[] persistentFields; - - internal FieldInfo[] PersistentFields - { - get { - if (persistentFields == null) { - var baseFields = Ancestor?.PersistentFields - ?? (IsEntity - ? GetBaseFields(UnderlyingType.BaseType, Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null)).ToArray() - : Array.Empty()); - - persistentFields = baseFields.Concat( - Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null) - .Except(baseFields).OrderBy(p => p.UnderlyingProperty.MetadataToken) - ).ToArray(); - } - return persistentFields; - } - } + internal FieldInfo[] PersistentFields => persistentFields ??= BuildPersistentFields(); /// /// Gets the field map for implemented interfaces. @@ -947,6 +917,33 @@ private IDictionary, FieldInfo> BuildStructureFieldMapping() return new ReadOnlyDictionary, FieldInfo>(result); } + + private IEnumerable GetBaseFields(Type type, IEnumerable fields) + { + if (type == typeof(Entity)) { + return new[] { Fields[nameof(Entity.TypeId)] }; + } + var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); + return GetBaseFields(type.BaseType, fields) + .Concat( + fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) + .OrderBy(p => p.UnderlyingProperty.MetadataToken) + ); + } + + private FieldInfo[] BuildPersistentFields() + { + var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null).ToArray(); + var baseFields = Ancestor?.PersistentFields + ?? (IsEntity + ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() + : Array.Empty()); + + return baseFields.Concat( + potentialFields.Except(baseFields).OrderBy(p => p.UnderlyingProperty.MetadataToken) + ).ToArray(); + } + #endregion /// From fc907823e63dc7edab164e8a1fae18af3a59f710 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:13:45 -0800 Subject: [PATCH 05/24] non-Public fields too --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 7ac961eafa..4b7d258717 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -923,7 +923,7 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f if (type == typeof(Entity)) { return new[] { Fields[nameof(Entity.TypeId)] }; } - var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); + var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); return GetBaseFields(type.BaseType, fields) .Concat( fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) From 0d0054486bf58b6aeb9bfb753dc1370c56e51997 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:15:35 -0800 Subject: [PATCH 06/24] add comment --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 4b7d258717..e114b9467e 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -926,6 +926,7 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); return GetBaseFields(type.BaseType, fields) .Concat( + // MetadataToken key would not be enough because of possible collisions between modules fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) .OrderBy(p => p.UnderlyingProperty.MetadataToken) ); From 6f7044be2bd601d9f28d11b571e54c5eeac5b63b Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 02:20:35 -0800 Subject: [PATCH 07/24] rename --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index e114b9467e..71722bdfa5 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -923,11 +923,11 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f if (type == typeof(Entity)) { return new[] { Fields[nameof(Entity.TypeId)] }; } - var tokens = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); + var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); return GetBaseFields(type.BaseType, fields) .Concat( // MetadataToken key would not be enough because of possible collisions between modules - fields.Where(p => tokens.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) + fields.Where(p => declared.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) .OrderBy(p => p.UnderlyingProperty.MetadataToken) ); } From 0f73f012750e5013665116c80ac1cbc5d94f2907 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 21:28:27 -0800 Subject: [PATCH 08/24] Fix issue witn `new` property specifier --- Directory.Build.props | 2 -- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 11 +++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 30cdf4850d..9385f6e535 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -108,8 +108,6 @@ - . true diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 71722bdfa5..782222debb 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -923,12 +923,15 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f if (type == typeof(Entity)) { return new[] { Fields[nameof(Entity.TypeId)] }; } - var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Select(p => (p.MetadataToken, p.Name)).ToHashSet(); + var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .ToDictionary(p => p.Name, p => p.MetadataToken); + return GetBaseFields(type.BaseType, fields) .Concat( - // MetadataToken key would not be enough because of possible collisions between modules - fields.Where(p => declared.Contains((p.UnderlyingProperty.MetadataToken, p.UnderlyingProperty.Name))) - .OrderBy(p => p.UnderlyingProperty.MetadataToken) + fields.Where(p => declared.ContainsKey(p.UnderlyingProperty.Name)) + .Select(p => (p, declared[p.UnderlyingProperty.Name])) + .OrderBy(t => t.Item2) + .Select(t => t.Item1.UnderlyingProperty.MetadataToken == t.Item2 ? t.Item1 : null) ); } From 1ba886e5eea18aaa21daee9c556168065cb5dcd4 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Nov 2023 21:41:15 -0800 Subject: [PATCH 09/24] Fix Flaky test --- ...tHub0110_SimpleCommandProcessorOverridesOriginalException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm.Tests/Issues/IssueGitHub0110_SimpleCommandProcessorOverridesOriginalException.cs b/Orm/Xtensive.Orm.Tests/Issues/IssueGitHub0110_SimpleCommandProcessorOverridesOriginalException.cs index a15a3085c3..2c0243adb5 100644 --- a/Orm/Xtensive.Orm.Tests/Issues/IssueGitHub0110_SimpleCommandProcessorOverridesOriginalException.cs +++ b/Orm/Xtensive.Orm.Tests/Issues/IssueGitHub0110_SimpleCommandProcessorOverridesOriginalException.cs @@ -142,7 +142,7 @@ public void MainTest() _ = theStarter.Set(); - while (!task1State.Ended && !task1State.Ended) { + while (!task1State.Ended || !task2State.Ended) { Thread.Sleep(100); } From 11355a36f1144802a8f2ff8df93b0e5699e94388 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Wed, 15 Nov 2023 00:02:31 -0800 Subject: [PATCH 10/24] Fix BuildPersistentFields() --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 782222debb..65846cf213 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -938,10 +938,9 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f private FieldInfo[] BuildPersistentFields() { var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null).ToArray(); - var baseFields = Ancestor?.PersistentFields - ?? (IsEntity + var baseFields = IsEntity ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : Array.Empty()); + : Array.Empty(); return baseFields.Concat( potentialFields.Except(baseFields).OrderBy(p => p.UnderlyingProperty.MetadataToken) From 13e73b1e59e10b8973f649b62abac17a0526a381 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 14:35:38 -0800 Subject: [PATCH 11/24] Skip overridden persistent properties --- .../Stages/ModifyPersistentTypesStage.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index 97724a9e07..f2a06b5c1d 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -109,20 +109,20 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor)); } - private static int NumberOfPersistentPropertis(TypeInfo type) => + private static int NumberOfPersistentProperties(TypeInfo type) => type is null ? 0 - : type.Properties.Values.Where(p => p.IsPersistent).Count() + NumberOfPersistentPropertis(type.BaseType); + : type.Properties.Values.Where(p => p.IsPersistent).Count() + NumberOfPersistentProperties(type.BaseType); private void ProcessFields(ProcessorContext context, TypeInfo type) { - int propsInBaseClass = NumberOfPersistentPropertis(type.BaseType); + int propsInBaseClass = NumberOfPersistentProperties(type.BaseType); if (type.Kind == PersistentTypeKind.Entity) { ++propsInBaseClass; // for TypeId } var propertyToIndex = type.Properties.Values - .Where(p => p.IsPersistent) + .Where(p => p.IsPersistent && (!p.IsOverride || !p.BaseProperty.IsPersistent)) // skip overridden persistent properties .OrderBy(p => p.Definition.MetadataToken.ToInt32()) .Select((p, idx) => KeyValuePair.Create(p, propsInBaseClass + idx)) .ToDictionary(kv => kv.Key, kv => kv.Value); From 510e7e2dd43876780ec0698b51e3138b9579c76b Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 14:55:28 -0800 Subject: [PATCH 12/24] Correctly process overriddent properties --- .../Stages/ModifyPersistentTypesStage.cs | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index f2a06b5c1d..213f2646ed 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -109,32 +109,34 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor)); } - private static int NumberOfPersistentProperties(TypeInfo type) => - type is null - ? 0 - : type.Properties.Values.Where(p => p.IsPersistent).Count() + NumberOfPersistentProperties(type.BaseType); - - private void ProcessFields(ProcessorContext context, TypeInfo type) + private Dictionary GetPropertyToIndexMap(ProcessorContext context, TypeInfo type) { - int propsInBaseClass = NumberOfPersistentProperties(type.BaseType); + if (type is null) { + return new(); + } + var r = GetPropertyToIndexMap(context, type.BaseType); + int idx = r.Count; if (type.Kind == PersistentTypeKind.Entity) { - ++propsInBaseClass; // for TypeId + ++idx; // for TypeId } + foreach (var p in type.Properties.Values + .Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context)) + .OrderBy(p => p.Definition.MetadataToken.ToInt32())) { + r[p] = p.IsOverride && p.BaseProperty.IsPersistent + ? r[p.BaseProperty] // For overridden persistent property assign base property's index + : idx++; + } + return r; + } - var propertyToIndex = type.Properties.Values - .Where(p => p.IsPersistent && (!p.IsOverride || !p.BaseProperty.IsPersistent)) // skip overridden persistent properties - .OrderBy(p => p.Definition.MetadataToken.ToInt32()) - .Select((p, idx) => KeyValuePair.Create(p, propsInBaseClass + idx)) - .ToDictionary(kv => kv.Key, kv => kv.Value); - - foreach (var property in type.Properties.Values.Where(p => p.IsPersistent)) { - if (!propertyChecker.ShouldProcess(property, context)) - continue; + private void ProcessFields(ProcessorContext context, TypeInfo type) + { + var typeDefinition = type.Definition; + var propertyToIndex = GetPropertyToIndexMap(context, type); + foreach (var property in type.Properties.Values.Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context))) { var persistentIndex = propertyToIndex[property]; - var typeDefinition = type.Definition; var propertyDefinition = property.Definition; - var persistentName = property.PersistentName ?? property.Name; // Backing field context.WeavingTasks.Add(new RemoveBackingFieldTask(typeDefinition, propertyDefinition)); // Getter @@ -149,7 +151,6 @@ private void ProcessFields(ProcessorContext context, TypeInfo type) if (property.PersistentName!=null) context.WeavingTasks.Add(new AddAttributeTask(propertyDefinition, context.References.OverrideFieldNameAttributeConstructor, property.PersistentName)); - ++persistentIndex; } } From 7026d428086ab7a64e90c96105d4359814a079f4 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 15:04:50 -0800 Subject: [PATCH 13/24] DOn't ignore abstract props --- .../Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index 213f2646ed..d42a94d2c7 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -119,8 +119,7 @@ private Dictionary GetPropertyToIndexMap(ProcessorContext con if (type.Kind == PersistentTypeKind.Entity) { ++idx; // for TypeId } - foreach (var p in type.Properties.Values - .Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context)) + foreach (var p in type.Properties.Values.Where(p => p.IsPersistent) .OrderBy(p => p.Definition.MetadataToken.ToInt32())) { r[p] = p.IsOverride && p.BaseProperty.IsPersistent ? r[p.BaseProperty] // For overridden persistent property assign base property's index From 3d54ef916e24b4467aaba76268d30d6f2f1b20b7 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 15:07:48 -0800 Subject: [PATCH 14/24] Add EOL --- Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs index 533275e5be..9eb195ec05 100644 --- a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs +++ b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs @@ -87,4 +87,4 @@ public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, Proper this.persistentIndex = persistentIndex; } } -} \ No newline at end of file +} From f4d63a9be356a22b575fe44f3498c655a330b7c1 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 16:17:51 -0800 Subject: [PATCH 15/24] Upgrade to Ubuntu 22.04 --- .github/actions/mssql-fts/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/mssql-fts/Dockerfile b/.github/actions/mssql-fts/Dockerfile index d9ec552cee..1f671b5f4f 100644 --- a/.github/actions/mssql-fts/Dockerfile +++ b/.github/actions/mssql-fts/Dockerfile @@ -4,7 +4,7 @@ USER root ENV ACCEPT_EULA=Y \ MSSQL_SA_PASSWORD=dbatools.I0 -RUN echo >>/etc/apt/sources.list "$(wget -qO- https://packages.microsoft.com/config/ubuntu/20.04/mssql-server-2022.list)" \ +RUN echo >>/etc/apt/sources.list "$(wget -qO- https://packages.microsoft.com/config/ubuntu/22.04/mssql-server-2022.list)" \ && apt-get update \ && apt-get install -y apt-utils mssql-server-fts From 988f3e6ab8ac1cca9d8d83eb1bc7f3aee2869b82 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 18:15:05 -0800 Subject: [PATCH 16/24] Fix assigning prop index --- .../Stages/ModifyPersistentTypesStage.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index d42a94d2c7..d92dab6ed2 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -115,9 +115,9 @@ private Dictionary GetPropertyToIndexMap(ProcessorContext con return new(); } var r = GetPropertyToIndexMap(context, type.BaseType); - int idx = r.Count; - if (type.Kind == PersistentTypeKind.Entity) { - ++idx; // for TypeId + int idx = r.Count == 0 ? 0 : r.Values.Max() + 1; + if (idx == 0 && type.Kind == PersistentTypeKind.Entity) { + idx = 1; // for TypeId } foreach (var p in type.Properties.Values.Where(p => p.IsPersistent) .OrderBy(p => p.Definition.MetadataToken.ToInt32())) { From baa55aca5bf9936d91aea3d8255cd36ffb032b1c Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 22:20:17 -0800 Subject: [PATCH 17/24] Fix processing virtual fields --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 65846cf213..b07da38e6e 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -917,24 +917,33 @@ private IDictionary, FieldInfo> BuildStructureFieldMapping() return new ReadOnlyDictionary, FieldInfo>(result); } - private IEnumerable GetBaseFields(Type type, IEnumerable fields) { if (type == typeof(Entity)) { return new[] { Fields[nameof(Entity.TypeId)] }; } + if (type == typeof(Structure)) { + return Array.Empty(); + } var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .ToDictionary(p => p.Name, p => p.MetadataToken); return GetBaseFields(type.BaseType, fields) .Concat( - fields.Where(p => declared.ContainsKey(p.UnderlyingProperty.Name)) - .Select(p => (p, declared[p.UnderlyingProperty.Name])) + fields.Select(p => (p, declared.TryGetValue(p.Name, out var token) ? token : 0)) + .Where(t => t.Item1.UnderlyingProperty.MetadataToken == t.Item2) .OrderBy(t => t.Item2) - .Select(t => t.Item1.UnderlyingProperty.MetadataToken == t.Item2 ? t.Item1 : null) + .Select(t => t.Item1) ); } + private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) => + a.Name == p.Name + && a.Name != "TypeId" + && p.IsInherited + && a.UnderlyingProperty.GetMethod?.IsVirtual == true + && p.UnderlyingProperty.GetMethod?.IsVirtual == true; + private FieldInfo[] BuildPersistentFields() { var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null).ToArray(); @@ -942,8 +951,14 @@ private FieldInfo[] BuildPersistentFields() ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() : Array.Empty(); - return baseFields.Concat( - potentialFields.Except(baseFields).OrderBy(p => p.UnderlyingProperty.MetadataToken) + var ancestorFields = Ancestor != null && Ancestor?.UnderlyingType != typeof(Structure) + ? Ancestor.PersistentFields + : Array.Empty(); + + return persistentFields = baseFields.Concat( + potentialFields.Where(p => p.DeclaringType == this && + (!baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) + .OrderBy(p => p.UnderlyingProperty.MetadataToken) ).ToArray(); } From 0cdcfedb4ca64322a6ceaad4c132d64027745d73 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 22:23:00 -0800 Subject: [PATCH 18/24] Minor fix --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index b07da38e6e..68c900b963 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -955,7 +955,7 @@ private FieldInfo[] BuildPersistentFields() ? Ancestor.PersistentFields : Array.Empty(); - return persistentFields = baseFields.Concat( + return baseFields.Concat( potentialFields.Where(p => p.DeclaringType == this && (!baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) .OrderBy(p => p.UnderlyingProperty.MetadataToken) From 31c86d8fe351e19388861caae1ff38c769eb1b77 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 16 Nov 2023 22:24:39 -0800 Subject: [PATCH 19/24] process IsStructure bases --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 68c900b963..58cd8daba2 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -947,7 +947,7 @@ private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) => private FieldInfo[] BuildPersistentFields() { var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null).ToArray(); - var baseFields = IsEntity + var baseFields = IsEntity || IsStructure ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() : Array.Empty(); From a33734148b718870846ed6585f54b0e6901c41b5 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 17 Nov 2023 01:36:36 -0800 Subject: [PATCH 20/24] Fix a few cases --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 43 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 58cd8daba2..bddcd1e1c8 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -917,12 +917,26 @@ private IDictionary, FieldInfo> BuildStructureFieldMapping() return new ReadOnlyDictionary, FieldInfo>(result); } - private IEnumerable GetBaseFields(Type type, IEnumerable fields) + private IEnumerable GetRootBaseFields(Type type, IEnumerable fields) { if (type == typeof(Entity)) { - return new[] { Fields[nameof(Entity.TypeId)] }; + return Array.Empty(); } - if (type == typeof(Structure)) { + var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .ToDictionary(p => p.Name, p => p.MetadataToken); + + return GetBaseFields(type.BaseType, fields) + .Concat( + fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0)) + .Where(t => t.Item2 != 0) + .OrderBy(t => t.Item2) + .Select(t => t.Item1) + ); + } + + private IEnumerable GetBaseFields(Type type, IEnumerable fields) + { + if (type == typeof(Entity) || type == typeof(Structure)) { return Array.Empty(); } var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) @@ -946,20 +960,27 @@ private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) => private FieldInfo[] BuildPersistentFields() { - var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null).ToArray(); - var baseFields = IsEntity || IsStructure - ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : Array.Empty(); + var propTypeId = IsEntity ? Fields[nameof(Entity.TypeId)] : null; + var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); + bool isRoot = Hierarchy?.Root == this; + var baseFields = + isRoot ? GetRootBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() + : IsEntity || IsStructure ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() + : Array.Empty(); var ancestorFields = Ancestor != null && Ancestor?.UnderlyingType != typeof(Structure) ? Ancestor.PersistentFields : Array.Empty(); - return baseFields.Concat( - potentialFields.Where(p => p.DeclaringType == this && - (!baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) + var props = baseFields.Concat( + potentialFields.Where(p => p.UnderlyingProperty.DeclaringType == UnderlyingType + && (isRoot || !baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) .OrderBy(p => p.UnderlyingProperty.MetadataToken) - ).ToArray(); + ); + if (IsEntity) { + props = props.Prepend(propTypeId); + } + return props.ToArray(); } #endregion From 4bfd05c51edbdefc8f6f59ef6b724f92497f4877 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sat, 18 Nov 2023 23:29:08 -0800 Subject: [PATCH 21/24] Fix QueryCompositeTest case --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 32 +++++++++++-------- .../Stages/ModifyPersistentTypesStage.cs | 6 ++-- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index bddcd1e1c8..ce651e067c 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -944,7 +944,7 @@ private IEnumerable GetBaseFields(Type type, IEnumerable f return GetBaseFields(type.BaseType, fields) .Concat( - fields.Select(p => (p, declared.TryGetValue(p.Name, out var token) ? token : 0)) + fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0)) .Where(t => t.Item1.UnderlyingProperty.MetadataToken == t.Item2) .OrderBy(t => t.Item2) .Select(t => t.Item1) @@ -961,23 +961,29 @@ private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) => private FieldInfo[] BuildPersistentFields() { var propTypeId = IsEntity ? Fields[nameof(Entity.TypeId)] : null; - var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); bool isRoot = Hierarchy?.Root == this; - var baseFields = - isRoot ? GetRootBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : IsEntity || IsStructure ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : Array.Empty(); - - var ancestorFields = Ancestor != null && Ancestor?.UnderlyingType != typeof(Structure) - ? Ancestor.PersistentFields - : Array.Empty(); - + var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); + FieldInfo[] baseFields; + FieldInfo[] ancestorFields = Array.Empty(); + if (Ancestor != null && Ancestor.UnderlyingType != typeof(Structure)) { + ancestorFields = Ancestor.PersistentFields; + baseFields = ancestorFields.Select(p => p != null && Fields.TryGetValue(p.Name, out var f) ? f : null).ToArray(); + } + else { + baseFields = + isRoot ? GetRootBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() + : IsEntity || IsStructure ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() + : Array.Empty(); + } var props = baseFields.Concat( potentialFields.Where(p => p.UnderlyingProperty.DeclaringType == UnderlyingType - && (isRoot || !baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) + && (isRoot + || p.IsExplicit + || !baseFields.Contains(p) + || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) .OrderBy(p => p.UnderlyingProperty.MetadataToken) ); - if (IsEntity) { + if (IsEntity && Ancestor == null) { props = props.Prepend(propTypeId); } return props.ToArray(); diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index d92dab6ed2..246fcd7794 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -40,8 +40,8 @@ public override ActionResult Execute(ProcessorContext context) new[] {references.Entity, references.FieldInfo}, }; - propertyChecker = (context.Language==SourceLanguage.CSharp) - ? (IPersistentPropertyChecker) new CsPropertyChecker() + propertyChecker = (context.Language==SourceLanguage.CSharp) + ? (IPersistentPropertyChecker) new CsPropertyChecker() : new VbPropertyChecker(); foreach (var type in context.PersistentTypes) @@ -121,7 +121,7 @@ private Dictionary GetPropertyToIndexMap(ProcessorContext con } foreach (var p in type.Properties.Values.Where(p => p.IsPersistent) .OrderBy(p => p.Definition.MetadataToken.ToInt32())) { - r[p] = p.IsOverride && p.BaseProperty.IsPersistent + r[p] = (p.IsOverride && p.BaseProperty.IsPersistent) ? r[p.BaseProperty] // For overridden persistent property assign base property's index : idx++; } From d0c5a43e62cab0e58c3f20b75e09a839166d0238 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sun, 19 Nov 2023 02:23:01 -0800 Subject: [PATCH 22/24] Process Recycled fields --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index ce651e067c..d1fd9efe8d 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -18,6 +18,7 @@ using Xtensive.Core; using Xtensive.Orm.Internals; using Xtensive.Orm.Validation; +using Xtensive.Reflection; using Xtensive.Tuples; using Xtensive.Tuples.Transform; using Tuple = Xtensive.Tuples.Tuple; @@ -963,6 +964,9 @@ private FieldInfo[] BuildPersistentFields() var propTypeId = IsEntity ? Fields[nameof(Entity.TypeId)] : null; bool isRoot = Hierarchy?.Root == this; var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); + var recycled = UnderlyingType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(p => p.GetAttribute() != null && !potentialFields.Any(pf => pf.UnderlyingProperty == p)).ToHashSet(); + FieldInfo[] baseFields; FieldInfo[] ancestorFields = Array.Empty(); if (Ancestor != null && Ancestor.UnderlyingType != typeof(Structure)) { @@ -981,7 +985,10 @@ private FieldInfo[] BuildPersistentFields() || p.IsExplicit || !baseFields.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) - .OrderBy(p => p.UnderlyingProperty.MetadataToken) + .Select(p => (p, p.UnderlyingProperty.MetadataToken)) + .Concat(recycled.Select(p => ((FieldInfo)null, p.MetadataToken))) + .OrderBy(t => t.Item2) + .Select(t => t.Item1) ); if (IsEntity && Ancestor == null) { props = props.Prepend(propTypeId); From 6284435e525fdb720d8db5b3d5c7f9e45171852c Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sun, 19 Nov 2023 03:32:35 -0800 Subject: [PATCH 23/24] Optimize Field mapping --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 2 +- Orm/Xtensive.Orm/Orm/Persistent.cs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index d1fd9efe8d..2c7913c84f 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -989,7 +989,7 @@ private FieldInfo[] BuildPersistentFields() .Concat(recycled.Select(p => ((FieldInfo)null, p.MetadataToken))) .OrderBy(t => t.Item2) .Select(t => t.Item1) - ); + ).Select(p => p?.ReflectedType.IsInterface == true ? FieldMap[p] : p); if (IsEntity && Ancestor == null) { props = props.Prepend(propTypeId); } diff --git a/Orm/Xtensive.Orm/Orm/Persistent.cs b/Orm/Xtensive.Orm/Orm/Persistent.cs index 2599b4bac4..a52d50d5e6 100644 --- a/Orm/Xtensive.Orm/Orm/Persistent.cs +++ b/Orm/Xtensive.Orm/Orm/Persistent.cs @@ -166,7 +166,7 @@ protected internal object GetFieldValue(string fieldName) } protected internal T GetFieldValue(int fieldIndex) => - GetFieldValue(TypeInfo.PersistentFields[fieldIndex]); + GetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex]); /// /// Gets the field value. @@ -176,10 +176,11 @@ protected internal T GetFieldValue(int fieldIndex) => /// Field value type. /// The field. /// Field value. - protected internal T GetFieldValue(FieldInfo field) + protected internal T GetFieldValue(FieldInfo field) => + GetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field); + + protected internal T GetNormalizedFieldValue(FieldInfo field) { - if (field.ReflectedType.IsInterface) - field = TypeInfo.FieldMap[field]; var fieldAccessor = GetNormalizedFieldAccessor(field); T result = default(T); try { @@ -362,7 +363,7 @@ protected internal void SetFieldValue(string fieldName, object value) } protected internal void SetFieldValue(int fieldIndex, T value) => - SetFieldValue(TypeInfo.PersistentFields[fieldIndex], value); + SetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex], value, null, null); /// /// Sets the field value. @@ -387,10 +388,11 @@ protected internal void SetFieldValue(FieldInfo field, object value) SetFieldValue(field, value, null, null); } - internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) + internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) => + SetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field, value, syncContext, removalContext); + + internal void SetNormalizedFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) { - if (field.ReflectedType.IsInterface) - field = TypeInfo.FieldMap[field]; SystemSetValueAttempt(field, value); var fieldAccessor = GetNormalizedFieldAccessor(field); object oldValue = GetNormalizedFieldValue(field, fieldAccessor); From eb0368546e95b989a77f9ae7ac7ff1b6d6268987 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 21 Nov 2023 11:40:08 -0800 Subject: [PATCH 24/24] Refactor --- Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs | 44 +++++++------------ .../Stages/ModifyPersistentTypesStage.cs | 6 +-- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index 2c7913c84f..9dc941a762 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -18,6 +18,7 @@ using Xtensive.Core; using Xtensive.Orm.Internals; using Xtensive.Orm.Validation; +using Xtensive.Orm.Upgrade; using Xtensive.Reflection; using Xtensive.Tuples; using Xtensive.Tuples.Transform; @@ -44,6 +45,10 @@ public sealed class TypeInfo : SchemaMappedNode /// public const int MinTypeId = 100; + private static readonly Type + TypeEntity = typeof(Entity) + , TypeStructure = typeof(Structure); + private static volatile int CurrentSharedId = 0; private static readonly ConcurrentDictionary TypeToSharedId = new(); @@ -918,35 +923,18 @@ private IDictionary, FieldInfo> BuildStructureFieldMapping() return new ReadOnlyDictionary, FieldInfo>(result); } - private IEnumerable GetRootBaseFields(Type type, IEnumerable fields) - { - if (type == typeof(Entity)) { - return Array.Empty(); - } - var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .ToDictionary(p => p.Name, p => p.MetadataToken); - - return GetBaseFields(type.BaseType, fields) - .Concat( - fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0)) - .Where(t => t.Item2 != 0) - .OrderBy(t => t.Item2) - .Select(t => t.Item1) - ); - } - - private IEnumerable GetBaseFields(Type type, IEnumerable fields) + private static IEnumerable GetBaseFields(Type type, IEnumerable fields, bool bRoot) { - if (type == typeof(Entity) || type == typeof(Structure)) { + if (type == TypeEntity || type == TypeStructure) { return Array.Empty(); } var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .ToDictionary(p => p.Name, p => p.MetadataToken); - return GetBaseFields(type.BaseType, fields) + return GetBaseFields(type.BaseType, fields, bRoot) .Concat( fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0)) - .Where(t => t.Item1.UnderlyingProperty.MetadataToken == t.Item2) + .Where(t => bRoot ? t.Item2 != 0 : t.Item1.UnderlyingProperty.MetadataToken == t.Item2) .OrderBy(t => t.Item2) .Select(t => t.Item1) ); @@ -965,25 +953,25 @@ private FieldInfo[] BuildPersistentFields() bool isRoot = Hierarchy?.Root == this; var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); var recycled = UnderlyingType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(p => p.GetAttribute() != null && !potentialFields.Any(pf => pf.UnderlyingProperty == p)).ToHashSet(); + .Where(p => p.GetAttribute() != null && !potentialFields.Any(pf => pf.UnderlyingProperty == p)); FieldInfo[] baseFields; FieldInfo[] ancestorFields = Array.Empty(); - if (Ancestor != null && Ancestor.UnderlyingType != typeof(Structure)) { + if (Ancestor != null && Ancestor.UnderlyingType != TypeStructure) { ancestorFields = Ancestor.PersistentFields; baseFields = ancestorFields.Select(p => p != null && Fields.TryGetValue(p.Name, out var f) ? f : null).ToArray(); } else { - baseFields = - isRoot ? GetRootBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : IsEntity || IsStructure ? GetBaseFields(UnderlyingType.BaseType, potentialFields).ToArray() - : Array.Empty(); + baseFields = !(IsEntity || IsStructure) + ? Array.Empty() + : GetBaseFields(UnderlyingType.BaseType, potentialFields, isRoot).ToArray(); } + var baseFieldsSet = baseFields.ToHashSet(); var props = baseFields.Concat( potentialFields.Where(p => p.UnderlyingProperty.DeclaringType == UnderlyingType && (isRoot || p.IsExplicit - || !baseFields.Contains(p) + || !baseFieldsSet.Contains(p) || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) .Select(p => (p, p.UnderlyingProperty.MetadataToken)) .Concat(recycled.Select(p => ((FieldInfo)null, p.MetadataToken))) diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index 246fcd7794..fc9d9659d2 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -109,12 +109,12 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor)); } - private Dictionary GetPropertyToIndexMap(ProcessorContext context, TypeInfo type) + private Dictionary GetPropertyToIndexMap(TypeInfo type) { if (type is null) { return new(); } - var r = GetPropertyToIndexMap(context, type.BaseType); + var r = GetPropertyToIndexMap(type.BaseType); int idx = r.Count == 0 ? 0 : r.Values.Max() + 1; if (idx == 0 && type.Kind == PersistentTypeKind.Entity) { idx = 1; // for TypeId @@ -131,7 +131,7 @@ private Dictionary GetPropertyToIndexMap(ProcessorContext con private void ProcessFields(ProcessorContext context, TypeInfo type) { var typeDefinition = type.Definition; - var propertyToIndex = GetPropertyToIndexMap(context, type); + var propertyToIndex = GetPropertyToIndexMap(type); foreach (var property in type.Properties.Values.Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context))) { var persistentIndex = propertyToIndex[property];