From ed8e13a1e05953c873f1f4d0d60397f741b63830 Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Tue, 14 Apr 2020 16:07:16 -0700 Subject: [PATCH 1/8] Improve performance of StructureExpression class --- .../Linq/Expressions/StructureExpression.cs | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs index af1a249702..e83d70f0d7 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs @@ -7,112 +7,128 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Model; using System.Linq; - namespace Xtensive.Orm.Linq.Expressions { internal sealed class StructureExpression : ParameterizedExpression, IPersistentExpression { private List fields; + private bool isNullable; + internal Segment Mapping; - public TypeInfo PersistentType { get; private set; } + public TypeInfo PersistentType { get; } - public bool IsNullable { get; set; } + public bool IsNullable => isNullable; public List Fields { - get { return fields; } - private set - { + get => fields; + private set { fields = value; - foreach (var fieldExpression in fields.OfType()) + foreach (var fieldExpression in fields.OfType()) { fieldExpression.Owner = this; + } } } public Expression Remap(int offset, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var mapping = new Segment(Mapping.Offset + offset, Mapping.Length); var result = new StructureExpression(PersistentType, mapping); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.Remap(offset, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We intentionally avoiding closure creation here + processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); + } result.Fields = processedFields; - result.IsNullable = IsNullable; + result.isNullable = isNullable; return result; } public Expression Remap(int[] map, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } - var result = new StructureExpression(PersistentType, default(Segment)); + var result = new StructureExpression(PersistentType, default); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.Remap(map, processedExpressions)) - .Where(f => f != null) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + var offset = int.MaxValue; + foreach (var field in fields) { + var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions); + if (mappedField == null) { + continue; + } + + var mappingOffset = mappedField.Mapping.Offset; + if (mappingOffset < offset) { + offset = mappingOffset; + } + + processedFields.Add(mappedField); + } + if (processedFields.Count == 0) { processedExpressions[this] = null; return null; } - var length = processedFields.Select(f => f.Mapping.Offset).Distinct().Count(); - var offset = processedFields.Min(f => f.Mapping.Offset); - result.Mapping = new Segment(offset, length); - result.Fields = processedFields; - result.IsNullable = IsNullable; + + result.Mapping = new Segment(offset, processedFields.Count); + result.Fields = processedFields; + result.isNullable = isNullable; return result; } public Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var result = new StructureExpression(PersistentType, Mapping); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.BindParameter(parameter, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We intentionally avoiding closure creation here + processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions)); + } result.Fields = processedFields; return result; } public Expression RemoveOuterParameter(Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var result = new StructureExpression(PersistentType, Mapping); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.RemoveOuterParameter(processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We intentionally avoiding closure creation here + processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions)); + } + result.Fields = processedFields; return result; } @@ -127,12 +143,12 @@ public static StructureExpression CreateLocalCollectionStructure(TypeInfo typeIn var destinationFields = new List(sourceFields.Count); var result = new StructureExpression(typeInfo, mapping) {Fields = destinationFields}; foreach (var field in sourceFields) { + // Do not convert to LINQ. We intentionally avoiding closure creation here destinationFields.Add(BuildNestedFieldExpression(field, mapping.Offset)); } return result; } -// ReSharper disable RedundantNameQualifier private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset) { if (nestedField.IsPrimitive) @@ -143,8 +159,6 @@ private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo ne return EntityFieldExpression.CreateEntityField(nestedField, offset); throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes)); } -// ReSharper restore RedundantNameQualifier - // Constructors @@ -153,7 +167,7 @@ private StructureExpression( in Segment mapping) : base(ExtendedExpressionType.Structure, persistentType.UnderlyingType, null, false) { - this.Mapping = mapping; + Mapping = mapping; PersistentType = persistentType; } } From d076a71072a30e3615d7da074e581c045268679b Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Tue, 14 Apr 2020 16:35:57 -0700 Subject: [PATCH 2/8] Improve performance of StructureFieldExpression class --- .../Expressions/StructureFieldExpression.cs | 125 ++++++++++-------- 1 file changed, 73 insertions(+), 52 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs index 232d5caed8..e45a7e4b1a 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs @@ -7,52 +7,49 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using Xtensive.Collections; using Xtensive.Core; using Xtensive.Orm.Model; using System.Linq; - namespace Xtensive.Orm.Linq.Expressions { internal sealed class StructureFieldExpression : FieldExpression, IPersistentExpression { private List fields; - public TypeInfo PersistentType { get; private set; } + public TypeInfo PersistentType { get; } - public bool IsNullable - { - get { return Owner != null && Owner.IsNullable; } - } + public bool IsNullable => Owner != null && Owner.IsNullable; public List Fields { - get { return fields; } - private set - { + get => fields; + private set { fields = value; - foreach (var fieldExpression in fields.OfType()) + foreach (var fieldExpression in fields.OfType()) { fieldExpression.Owner = this; + } } } public override Expression Remap(int offset, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var newMapping = new Segment(Mapping.Offset + offset, Mapping.Length); var result = new StructureFieldExpression(PersistentType, Field, newMapping, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.Remap(offset, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); + } if (Owner == null) { result.fields = processedFields; return result; @@ -65,27 +62,38 @@ public override Expression Remap(int offset, Dictionary public override Expression Remap(int[] map, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } - var result = new StructureFieldExpression(PersistentType, Field, default(Segment), OuterParameter, DefaultIfEmpty); + var result = new StructureFieldExpression(PersistentType, Field, default, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.Remap(map, processedExpressions)) - .Where(f => f != null) - .Cast() - .ToList(); + var offset = int.MaxValue; + var processedFields = new List(fields.Count); + foreach (var field in fields) { + var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions); + if (mappedField == null) { + continue; + } + + var mappingOffset = mappedField.Mapping.Offset; + if (mappingOffset < offset) { + offset = mappingOffset; + } + + processedFields.Add(mappedField); + } + if (processedFields.Count == 0) { processedExpressions[this] = null; return null; } - var length = processedFields.Select(f => f.Mapping.Offset).Distinct().Count(); - var offset = processedFields.Min(f => f.Mapping.Offset); - result.Mapping = new Segment(offset, length); + + result.Mapping = new Segment(offset, processedFields.Count); if (Owner == null) { result.fields = processedFields; return result; @@ -97,16 +105,18 @@ public override Expression Remap(int[] map, Dictionary p public override Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var result = new StructureFieldExpression(PersistentType, Field, Mapping, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.BindParameter(parameter, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions)); + } + if (Owner == null) { result.fields = processedFields; return result; @@ -119,16 +129,18 @@ public override Expression BindParameter(ParameterExpression parameter, Dictiona public override Expression RemoveOuterParameter(Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var result = new StructureFieldExpression(PersistentType, Field, Mapping, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); - var processedFields = Fields - .Select(f => f.RemoveOuterParameter(processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions)); + } + if (Owner == null) { result.fields = processedFields; return result; @@ -141,26 +153,35 @@ public override Expression RemoveOuterParameter(Dictionary() - .Select(f => (PersistentFieldExpression)f.RemoveOwner()) - .ToList(); + } + + var result = new StructureFieldExpression(PersistentType, Field, Mapping, OuterParameter, DefaultIfEmpty) { + fields = new List(fields.Count) + }; + foreach (var field in fields) { + result.fields.Add(((FieldExpression) field).RemoveOwner()); + } return result; } public static StructureFieldExpression CreateStructure(FieldInfo structureField, int offset) { - if (!structureField.IsStructure) + if (!structureField.IsStructure) { throw new ArgumentException(string.Format(Strings.ExFieldIsNotStructure, structureField.Name)); + } + var persistentType = structureField.ReflectedType.Model.Types[structureField.ValueType]; var mapping = new Segment(offset + structureField.MappingInfo.Offset, structureField.MappingInfo.Length); var result = new StructureFieldExpression(persistentType, structureField, mapping, null, false); - result.Fields = persistentType.Fields - .Select(f => BuildNestedFieldExpression(f, offset + structureField.MappingInfo.Offset)) - .ToList(); + var processedFields = new List(persistentType.Fields.Count); + foreach (var field in persistentType.Fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add(BuildNestedFieldExpression(field, offset + structureField.MappingInfo.Offset)); + } + + result.Fields = processedFields; return result; } From c581a6a67f6c87f7808dc2fb49c5d37cb042720e Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Tue, 14 Apr 2020 17:11:12 -0700 Subject: [PATCH 3/8] Improve performance of EntityExpression class --- .../Orm/Linq/Expressions/EntityExpression.cs | 146 +++++++++++------- 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs index b9fb1d2f22..929dda822a 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs @@ -19,19 +19,18 @@ internal class EntityExpression : ParameterizedExpression, { private List fields; - public TypeInfo PersistentType { get; private set; } + public TypeInfo PersistentType { get; } - public KeyExpression Key { get; private set; } + public KeyExpression Key { get; } public List Fields { - get { return fields; } - private set - { + get => fields; + private set { fields = value; - var fieldExpressions = fields.OfType(); - foreach (var fieldExpression in fieldExpressions) + foreach (var fieldExpression in fields.OfType()) { fieldExpression.Owner = this; + } } } @@ -39,74 +38,96 @@ private set public Expression Remap(int offset, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + } + + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var keyExpression = (KeyExpression) Key.Remap(offset, processedExpressions); var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); result.IsNullable = IsNullable; - result.Fields = Fields - .Select(f => f.Remap(offset, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); + } + + result.Fields = processedFields; return result; } public Expression Remap(int[] map, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + } + + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var keyExpression = (KeyExpression) Key.Remap(map, processedExpressions); - if (keyExpression==null) + if (keyExpression==null) { return null; + } + var result = new EntityExpression(PersistentType, keyExpression, OuterParameter, DefaultIfEmpty); processedExpressions.Add(this, result); result.IsNullable = IsNullable; - result.Fields = Fields - .Select(f => f.Remap(map, processedExpressions)) - .Where(f => f!=null) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions); + if (mappedField == null) { + continue; + } + + processedFields.Add(mappedField); + } + + result.Fields = processedFields; return result; } public Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var keyExpression = (KeyExpression) Key.BindParameter(parameter, processedExpressions); var result = new EntityExpression(PersistentType, keyExpression, parameter, DefaultIfEmpty); processedExpressions.Add(this, result); - result.Fields = Fields - .Select(f => f.BindParameter(parameter, processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions)); + } + + result.Fields = processedFields; return result; } public Expression RemoveOuterParameter(Dictionary processedExpressions) { - Expression value; - if (processedExpressions.TryGetValue(this, out value)) + if (processedExpressions.TryGetValue(this, out var value)) { return value; + } var keyExpression = (KeyExpression) Key.RemoveOuterParameter(processedExpressions); var result = new EntityExpression(PersistentType, keyExpression, null, DefaultIfEmpty); processedExpressions.Add(this, result); - result.Fields = Fields - .Select(f => f.RemoveOuterParameter(processedExpressions)) - .Cast() - .ToList(); + var processedFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + processedFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions)); + } + + result.Fields = processedFields; return result; } @@ -127,23 +148,31 @@ public static void Fill(EntityExpression entityExpression, int offset) public static EntityExpression Create(TypeInfo typeInfo, int offset, bool keyFieldsOnly) { - if (!typeInfo.IsEntity && !typeInfo.IsInterface) - throw new ArgumentException(string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), "typeInfo"); - var fields = new List(); + if (!typeInfo.IsEntity && !typeInfo.IsInterface) { + throw new ArgumentException( + string.Format(Strings.ExPersistentTypeXIsNotEntityOrPersistentInterface, typeInfo.Name), nameof(typeInfo)); + } + var keyExpression = KeyExpression.Create(typeInfo, offset); - fields.Add(keyExpression); + + List fields; var result = new EntityExpression(typeInfo, keyExpression, null, false); if (keyFieldsOnly) { + fields = new List(keyExpression.KeyFields.Count + 1) {keyExpression}; // Add key fields to field collection - var keyFieldClones = keyExpression - .KeyFields - .Select(kf=>FieldExpression.CreateField(kf.Field, offset)) - .Cast(); - fields.AddRange(keyFieldClones); - } - else - foreach (var nestedField in typeInfo.Fields) + foreach (var keyField in keyExpression.KeyFields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + fields.Add(FieldExpression.CreateField(keyField.Field, offset)); + } + } + else { + fields = new List(typeInfo.Fields.Count + 1) {keyExpression}; + foreach (var nestedField in typeInfo.Fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. fields.Add(BuildNestedFieldExpression(nestedField, offset)); + } + } + result.Fields = fields; return result; } @@ -151,17 +180,22 @@ public static EntityExpression Create(TypeInfo typeInfo, int offset, bool keyFie public static EntityExpression Create(EntityFieldExpression entityFieldExpression, int offset) { var typeInfo = entityFieldExpression.PersistentType; - var fields = new List(); var keyExpression = KeyExpression.Create(typeInfo, offset); - fields.Add(keyExpression); - foreach (var nestedField in typeInfo.Fields) - fields.Add(BuildNestedFieldExpression(nestedField, offset)); + var fields = new List(typeInfo.Fields.Count + 1) {keyExpression}; + foreach (var nestedField in typeInfo.Fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + fields.Add(BuildNestedFieldExpression(nestedField, offset)); + } + var result = new EntityExpression(typeInfo, keyExpression, null, entityFieldExpression.DefaultIfEmpty) { Fields = fields }; - if (entityFieldExpression.OuterParameter==null) + if (entityFieldExpression.OuterParameter == null) { return result; - return (EntityExpression) result.BindParameter(entityFieldExpression.OuterParameter, new Dictionary()); + } + + return (EntityExpression) result.BindParameter( + entityFieldExpression.OuterParameter, new Dictionary()); } private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset) @@ -177,11 +211,7 @@ private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo ne throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes)); } - public override string ToString() - { - return string.Format("{0} {1}", base.ToString(), PersistentType.Name); - } - + public override string ToString() => $"{base.ToString()} {PersistentType.Name}"; // Constructors From 27ccc0417084cd7636177e7d22579f9002ce6015 Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Tue, 14 Apr 2020 17:29:49 -0700 Subject: [PATCH 4/8] Improve performance of EntityFieldExpression class --- .../Linq/Expressions/EntityFieldExpression.cs | 142 ++++++++++-------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs index 9fd81d67bb..c820690ef1 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs @@ -16,15 +16,14 @@ namespace Xtensive.Orm.Linq.Expressions internal sealed class EntityFieldExpression : FieldExpression, IEntityExpression { - public TypeInfo PersistentType { get; private set; } - public List Fields { get; private set; } - public KeyExpression Key { get; private set; } + private readonly List fields; + + public TypeInfo PersistentType { get; } + public List Fields => fields; + public KeyExpression Key { get; } public EntityExpression Entity { get; private set; } - public bool IsNullable - { - get { return Owner != null && Owner.IsNullable || Field.IsNullable; } - } + public bool IsNullable => (Owner != null && Owner.IsNullable) || Field.IsNullable; public void RegisterEntityExpression(int offset) { @@ -34,24 +33,27 @@ public void RegisterEntityExpression(int offset) public override Expression Remap(int offset, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } + + var newFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + newFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); + } - var fields = Fields - .Select(f => f.Remap(offset, processedExpressions)) - .Cast() - .ToList(); var keyExpression = (KeyExpression) Key.Remap(offset, processedExpressions); - var entity = Entity!=null - ? (EntityExpression) Entity.Remap(offset, processedExpressions) - : null; - result = new EntityFieldExpression(PersistentType, Field, fields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); - if (Owner==null) + var entity = (EntityExpression) Entity?.Remap(offset, processedExpressions); + result = new EntityFieldExpression( + PersistentType, Field, newFields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); + if (Owner==null) { return result; + } processedExpressions.Add(this, result); Owner.Remap(offset, processedExpressions); @@ -60,57 +62,68 @@ public override Expression Remap(int offset, Dictionary public override Expression Remap(int[] map, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } - List fields; + var newFields = new List(fields.Count); using (new SkipOwnerCheckScope()) { - fields = Fields - .Select(f => f.Remap(map, processedExpressions)) - .Where(f => f!=null) - .Cast() - .ToList(); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + var mappedField = (PersistentFieldExpression) field.Remap(map, processedExpressions); + if (mappedField == null) { + continue; + } + + newFields.Add(mappedField); + } } - if (fields.Count!=Fields.Count) { + + if (newFields.Count!=Fields.Count) { processedExpressions.Add(this, null); return null; } + var keyExpression = (KeyExpression) Key.Remap(map, processedExpressions); EntityExpression entity; - using (new SkipOwnerCheckScope()) - entity = Entity!=null - ? (EntityExpression) Entity.Remap(map, processedExpressions) - : null; - result = new EntityFieldExpression(PersistentType, Field, fields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); - if (Owner==null) + using (new SkipOwnerCheckScope()) { + entity = (EntityExpression) Entity?.Remap(map, processedExpressions); + } + + result = new EntityFieldExpression( + PersistentType, Field, newFields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); + if (Owner==null) { return result; + } processedExpressions.Add(this, result); Owner.Remap(map, processedExpressions); return result; } - public override Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) + public override Expression BindParameter( + ParameterExpression parameter, Dictionary processedExpressions) { - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } - var fields = Fields - .Select(f => f.BindParameter(parameter, processedExpressions)) - .Cast() - .ToList(); + var newFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + newFields.Add((PersistentFieldExpression) field.BindParameter(parameter, processedExpressions)); + } var keyExpression = (KeyExpression) Key.BindParameter(parameter, processedExpressions); - var entity = Entity!=null - ? (EntityExpression) Entity.BindParameter(parameter, processedExpressions) - : null; - result = new EntityFieldExpression(PersistentType, Field, fields, Mapping, keyExpression, entity, parameter, DefaultIfEmpty); - if (Owner==null) + var entity = (EntityExpression) Entity?.BindParameter(parameter, processedExpressions); + result = new EntityFieldExpression( + PersistentType, Field, newFields, Mapping, keyExpression, entity, parameter, DefaultIfEmpty); + if (Owner==null) { return result; + } processedExpressions.Add(this, result); Owner.BindParameter(parameter, processedExpressions); @@ -119,21 +132,22 @@ public override Expression BindParameter(ParameterExpression parameter, Dictiona public override Expression RemoveOuterParameter(Dictionary processedExpressions) { - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } - var fields = Fields - .Select(f => f.RemoveOuterParameter(processedExpressions)) - .Cast() - .ToList(); + var newFields = new List(fields.Count); + foreach (var field in fields) { + // Do not convert to LINQ. We want to avoid a closure creation here. + newFields.Add((PersistentFieldExpression) field.RemoveOuterParameter(processedExpressions)); + } var keyExpression = (KeyExpression) Key.RemoveOuterParameter(processedExpressions); - var entity = Entity!=null - ? (EntityExpression) Entity.RemoveOuterParameter(processedExpressions) - : null; - result = new EntityFieldExpression(PersistentType, Field, fields, Mapping, keyExpression, entity, null, DefaultIfEmpty); - if (Owner==null) + var entity = (EntityExpression) Entity?.RemoveOuterParameter(processedExpressions); + result = new EntityFieldExpression( + PersistentType, Field, newFields, Mapping, keyExpression, entity, null, DefaultIfEmpty); + if (Owner==null) { return result; + } processedExpressions.Add(this, result); Owner.RemoveOuterParameter(processedExpressions); @@ -173,18 +187,18 @@ private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo ne // Constructors private EntityFieldExpression( - TypeInfo persistentType, - FieldInfo field, + TypeInfo persistentType, + FieldInfo field, List fields, in Segment mapping, - KeyExpression key, - EntityExpression entity, - ParameterExpression parameterExpression, + KeyExpression key, + EntityExpression entity, + ParameterExpression parameterExpression, bool defaultIfEmpty) : base(ExtendedExpressionType.EntityField, field, mapping, parameterExpression, defaultIfEmpty) { PersistentType = persistentType; - Fields = fields; + this.fields = fields; Key = key; Entity = entity; } From e34e33d97a96cfc21cc8ee631c56dff3ba7e453c Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Tue, 14 Apr 2020 16:50:00 -0700 Subject: [PATCH 5/8] Improve performance of ItemProjectorExpression class --- .../Expressions/ItemProjectorExpression.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs index 5864146e56..d4cf00ae7a 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs @@ -5,7 +5,6 @@ // Created: 2009.05.06 using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using Xtensive.Core; using Xtensive.Orm.Linq.Expressions.Visitors; @@ -139,9 +138,12 @@ public ItemProjectorExpression EnsureEntityIsJoined() var joinedIndex = typeInfo.Indexes.PrimaryIndex; var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); var keySegment = entityExpression.Key.Mapping; - var keyPairs = keySegment.GetItems() - .Select((leftIndex, rightIndex) => new Pair(leftIndex, rightIndex)) - .ToArray(); + var keyPairs = new Pair[keySegment.Length]; + var rightIndex = 0; + foreach (var leftIndex in keySegment.GetItems()) { + keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); + rightIndex++; + } var offset = dataSource.Header.Length; var dataSourceAsJoin = dataSource as JoinProvider; dataSource = entityExpression.IsNullable || (dataSourceAsJoin!=null && dataSourceAsJoin.JoinType==JoinType.LeftOuter) @@ -158,9 +160,12 @@ public ItemProjectorExpression EnsureEntityIsJoined() var joinedIndex = typeInfo.Indexes.PrimaryIndex; var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); var keySegment = entityFieldExpression.Mapping; - var keyPairs = keySegment.GetItems() - .Select((leftIndex, rightIndex) => new Pair(leftIndex, rightIndex)) - .ToArray(); + var keyPairs = new Pair[keySegment.Length]; + var rightIndex = 0; + foreach (var leftIndex in keySegment.GetItems()) { + keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); + rightIndex++; + } var offset = dataSource.Header.Length; var dataSourceAsJoin = dataSource as JoinProvider; dataSource = entityFieldExpression.IsNullable || (dataSourceAsJoin!=null && dataSourceAsJoin.JoinType==JoinType.LeftOuter) From e0adb6ba955f9d2c4120ad6e3c221d69b3fe5637 Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Wed, 15 Apr 2020 19:23:45 -0700 Subject: [PATCH 6/8] Improve performance of EntityFieldExpression.CreateEntityField method --- .../Linq/Expressions/EntityFieldExpression.cs | 22 ++++++++++++------- .../Orm/Linq/Expressions/FieldExpression.cs | 7 ++++-- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 12 +++++----- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs index c820690ef1..f8e815779f 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs @@ -161,16 +161,22 @@ public override FieldExpression RemoveOwner() public static EntityFieldExpression CreateEntityField(FieldInfo entityField, int offset) { - if (!entityField.IsEntity) - throw new ArgumentException(string.Format(Strings.ExFieldXIsNotEntity, entityField.Name), "entityField"); + if (!entityField.IsEntity) { + throw new ArgumentException(string.Format(Strings.ExFieldXIsNotEntity, entityField.Name), nameof(entityField)); + } + var entityType = entityField.ValueType; var persistentType = entityField.ReflectedType.Model.Types[entityType]; - var mapping = new Segment(entityField.MappingInfo.Offset + offset, entityField.MappingInfo.Length); - var fields = new List(); - var keyExpression = KeyExpression.Create(persistentType, offset + entityField.MappingInfo.Offset); - fields.Add(keyExpression); - foreach (var keyField in persistentType.Fields.Where(f => f.IsPrimaryKey)) - fields.Add(BuildNestedFieldExpression(keyField, offset + entityField.MappingInfo.Offset)); + + ref var mappingInfo = ref entityField.mappingInfo; + var mapping = new Segment(mappingInfo.Offset + offset, mappingInfo.Length); + var keyFields = persistentType.Key.Fields; + var keyExpression = KeyExpression.Create(persistentType, offset + mappingInfo.Offset); + var fields = new List(keyFields.Count + 1) {keyExpression}; + foreach (var field in keyFields) { + fields.Add(BuildNestedFieldExpression(field, offset + mappingInfo.Offset)); + } + return new EntityFieldExpression(persistentType, entityField, fields, mapping, keyExpression, null, null, false); } diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs index a6062c66ba..d8bb0642d4 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs @@ -119,9 +119,12 @@ public virtual FieldExpression RemoveOwner() public static FieldExpression CreateField(FieldInfo field, int offset) { - if (!field.IsPrimitive) + if (!field.IsPrimitive) { throw new ArgumentException(string.Format(Strings.ExFieldXIsNotPrimitive, field.Name), "field"); - var mapping = new Segment(field.MappingInfo.Offset + offset, field.MappingInfo.Length); + } + + ref var mappingInfo = ref field.mappingInfo; + var mapping = new Segment(mappingInfo.Offset + offset, mappingInfo.Length); return new FieldExpression(ExtendedExpressionType.Field, field, mapping, null, false); } diff --git a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs index b2e32a13b3..1f8622dfdb 100644 --- a/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs @@ -60,7 +60,7 @@ public sealed class FieldInfo : MappedNode, private int? cachedHashCode; private IList validators; - private Segment mappingInfo; + internal Segment mappingInfo; #region IsXxx properties @@ -720,14 +720,14 @@ private void CreateMappingInfo() mappingInfo = new Segment(primaryIndex.Columns.IndexOf(indexColumn), 1); } } - else - if (Fields.Count > 0) - mappingInfo = new Segment( - Fields.First().MappingInfo.Offset, Fields.Sum(f => f.IsPrimitive ? f.MappingInfo.Length : 0)); + else if (Fields.Count > 0) { + mappingInfo = new Segment( + Fields[0].mappingInfo.Offset, Fields.Sum(f => f.IsPrimitive ? f.mappingInfo.Length : 0)); + } if (IsEntity || IsStructure) { valueExtractor = new SegmentTransform( - false, reflectedType.TupleDescriptor, new Segment(MappingInfo.Offset, MappingInfo.Length)); + false, reflectedType.TupleDescriptor, new Segment(mappingInfo.Offset, mappingInfo.Length)); } } From 90528fc82b13512aee403d730d77f4b0d7dd22f0 Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Wed, 15 Apr 2020 20:08:37 -0700 Subject: [PATCH 7/8] Use ordinal string comparison in ItemProjectorExpression.EnsureEntityIsJoined method --- .../Orm/Linq/Expressions/ItemProjectorExpression.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs index d4cf00ae7a..fa91426be6 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs @@ -4,6 +4,7 @@ // Created by: Alexis Kochetov // Created: 2009.05.06 +using System; using System.Collections.Generic; using System.Linq.Expressions; using Xtensive.Core; @@ -122,7 +123,7 @@ public ItemProjectorExpression EnsureEntityIsJoined() foreach (var fieldInfo in typeInfo.Fields) { var isUsedInEntityExpression = false; foreach (var entityField in entityExpression.Fields) { - if (entityField.Name == fieldInfo.Name) { + if (string.Equals(entityField.Name, fieldInfo.Name, StringComparison.Ordinal)) { isUsedInEntityExpression = true; break; } From ac43303931575c108e2f4aed520a671620c6a972 Mon Sep 17 00:00:00 2001 From: AlexUstinov Date: Wed, 5 Aug 2020 22:20:32 -0700 Subject: [PATCH 8/8] Formatting and copyright comments fixes --- .../Orm/Linq/Expressions/EntityExpression.cs | 33 +- .../Linq/Expressions/EntityFieldExpression.cs | 32 +- .../Orm/Linq/Expressions/FieldExpression.cs | 53 +-- .../Expressions/ItemProjectorExpression.cs | 419 +++++++++--------- .../Linq/Expressions/StructureExpression.cs | 21 +- .../Expressions/StructureFieldExpression.cs | 23 +- Orm/Xtensive.Orm/Orm/Model/FieldInfo.cs | 6 +- 7 files changed, 308 insertions(+), 279 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs index 929dda822a..d6887428bd 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityExpression.cs @@ -1,15 +1,13 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-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: Alexis Kochetov // Created: 2009.05.05 using System; -using System.Diagnostics; using System.Linq; using System.Collections.Generic; using System.Linq.Expressions; -using Xtensive.Core; using Xtensive.Orm.Model; namespace Xtensive.Orm.Linq.Expressions @@ -71,7 +69,7 @@ public Expression Remap(int[] map, Dictionary processedE } var keyExpression = (KeyExpression) Key.Remap(map, processedExpressions); - if (keyExpression==null) { + if (keyExpression == null) { return null; } @@ -139,9 +137,10 @@ public static void Fill(EntityExpression entityExpression, int offset) var typeInfo = entityExpression.PersistentType; foreach (var nestedField in typeInfo.Fields.Except(entityExpression.Fields.OfType().Select(field=>field.Field))) { var nestedFieldExpression = BuildNestedFieldExpression(nestedField, offset); - var fieldExpression = nestedFieldExpression as FieldExpression; - if (fieldExpression!=null) + if (nestedFieldExpression is FieldExpression fieldExpression) { fieldExpression.Owner = entityExpression; + } + entityExpression.fields.Add(nestedFieldExpression); } } @@ -200,14 +199,22 @@ public static EntityExpression Create(EntityFieldExpression entityFieldExpressio private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset) { - if (nestedField.IsPrimitive) + if (nestedField.IsPrimitive) { return FieldExpression.CreateField(nestedField, offset); - if (nestedField.IsStructure) + } + + if (nestedField.IsStructure) { return StructureFieldExpression.CreateStructure(nestedField, offset); - if (nestedField.IsEntity) + } + + if (nestedField.IsEntity) { return EntityFieldExpression.CreateEntityField(nestedField, offset); - if (nestedField.IsEntitySet) - return EntitySetExpression.CreateEntitySet(nestedField); + } + + if (nestedField.IsEntitySet) { + return EntitySetExpression.CreateEntitySet(nestedField); + } + throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes)); } diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs index f8e815779f..867bac656f 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs @@ -1,12 +1,11 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-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: Alexis Kochetov // Created: 2009.05.06 using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using Xtensive.Core; using Xtensive.Orm.Model; @@ -51,7 +50,7 @@ public override Expression Remap(int offset, Dictionary var entity = (EntityExpression) Entity?.Remap(offset, processedExpressions); result = new EntityFieldExpression( PersistentType, Field, newFields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); - if (Owner==null) { + if (Owner == null) { return result; } @@ -83,7 +82,7 @@ public override Expression Remap(int[] map, Dictionary p } } - if (newFields.Count!=Fields.Count) { + if (newFields.Count != Fields.Count) { processedExpressions.Add(this, null); return null; } @@ -96,7 +95,7 @@ public override Expression Remap(int[] map, Dictionary p result = new EntityFieldExpression( PersistentType, Field, newFields, keyExpression.Mapping, keyExpression, entity, OuterParameter, DefaultIfEmpty); - if (Owner==null) { + if (Owner == null) { return result; } @@ -121,7 +120,7 @@ public override Expression BindParameter( var entity = (EntityExpression) Entity?.BindParameter(parameter, processedExpressions); result = new EntityFieldExpression( PersistentType, Field, newFields, Mapping, keyExpression, entity, parameter, DefaultIfEmpty); - if (Owner==null) { + if (Owner == null) { return result; } @@ -145,7 +144,7 @@ public override Expression RemoveOuterParameter(Dictionary + new EntityFieldExpression(PersistentType, Field, Fields, Mapping, Key, Entity, OuterParameter, DefaultIfEmpty); public static EntityFieldExpression CreateEntityField(FieldInfo entityField, int offset) { @@ -174,6 +171,7 @@ public static EntityFieldExpression CreateEntityField(FieldInfo entityField, int var keyExpression = KeyExpression.Create(persistentType, offset + mappingInfo.Offset); var fields = new List(keyFields.Count + 1) {keyExpression}; foreach (var field in keyFields) { + // Do not convert to LINQ. We want to avoid a closure creation here. fields.Add(BuildNestedFieldExpression(field, offset + mappingInfo.Offset)); } @@ -182,10 +180,14 @@ public static EntityFieldExpression CreateEntityField(FieldInfo entityField, int private static PersistentFieldExpression BuildNestedFieldExpression(FieldInfo nestedField, int offset) { - if (nestedField.IsPrimitive) + if (nestedField.IsPrimitive) { return CreateField(nestedField, offset); - if (nestedField.IsEntity) + } + + if (nestedField.IsEntity) { return CreateEntityField(nestedField, offset); + } + throw new NotSupportedException(string.Format(Strings.ExNestedFieldXIsNotSupported, nestedField.Attributes)); } diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs index d8bb0642d4..0f38f6816c 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs @@ -1,16 +1,14 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-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: Alexis Kochetov // Created: 2009.05.05 using System; using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; using Xtensive.Core; using FieldInfo=Xtensive.Orm.Model.FieldInfo; -using Xtensive.Collections; namespace Xtensive.Orm.Linq.Expressions { @@ -23,9 +21,8 @@ internal class FieldExpression : PersistentFieldExpression public virtual IPersistentExpression Owner { get => owner; - internal set - { - if (owner!=null) { + internal set { + if (owner != null) { throw Exceptions.AlreadyInitialized("Owner"); } @@ -56,26 +53,32 @@ public override Expression Remap(int offset, Dictionary public override Expression Remap(int[] map, Dictionary processedExpressions) { - if (!CanRemap) + if (!CanRemap) { return this; + } - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } var offset = map.IndexOf(Mapping.Offset); if (offset < 0) { - if (owner == null && !SkipOwnerCheckScope.IsActive) + if (owner == null && !SkipOwnerCheckScope.IsActive) { throw new InvalidOperationException(Strings.ExUnableToRemapFieldExpression); + } + processedExpressions.Add(this, null); - if (owner != null) + if (owner != null) { Owner.Remap(map, processedExpressions); + } + return null; } var newMapping = new Segment(offset, Mapping.Length); result = new FieldExpression(ExtendedExpressionType.Field, Field, newMapping, OuterParameter, DefaultIfEmpty); - if (owner == null) + if (owner == null) { return result; + } processedExpressions.Add(this, result); Owner.Remap(map, processedExpressions); @@ -84,13 +87,14 @@ public override Expression Remap(int[] map, Dictionary p public override Expression BindParameter(ParameterExpression parameter, Dictionary processedExpressions) { - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } result = new FieldExpression(ExtendedExpressionType.Field, Field, Mapping, parameter, DefaultIfEmpty); - if (owner == null) + if (owner == null) { return result; + } processedExpressions.Add(this, result); Owner.BindParameter(parameter, processedExpressions); @@ -99,28 +103,27 @@ public override Expression BindParameter(ParameterExpression parameter, Dictiona public override Expression RemoveOuterParameter(Dictionary processedExpressions) { - Expression result; - if (processedExpressions.TryGetValue(this, out result)) + if (processedExpressions.TryGetValue(this, out var result)) { return result; + } result = new FieldExpression(ExtendedExpressionType.Field, Field, Mapping, null, DefaultIfEmpty); - if (owner == null) + if (owner == null) { return result; + } processedExpressions.Add(this, result); Owner.RemoveOuterParameter(processedExpressions); return result; } - public virtual FieldExpression RemoveOwner() - { - return new FieldExpression(ExtendedExpressionType.Field, Field, Mapping, OuterParameter, DefaultIfEmpty); - } + public virtual FieldExpression RemoveOwner() => + new FieldExpression(ExtendedExpressionType.Field, Field, Mapping, OuterParameter, DefaultIfEmpty); public static FieldExpression CreateField(FieldInfo field, int offset) { if (!field.IsPrimitive) { - throw new ArgumentException(string.Format(Strings.ExFieldXIsNotPrimitive, field.Name), "field"); + throw new ArgumentException(string.Format(Strings.ExFieldXIsNotPrimitive, field.Name), nameof(field)); } ref var mappingInfo = ref field.mappingInfo; diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs index fa91426be6..e013f5b9c5 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs @@ -1,210 +1,209 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. -// Created by: Alexis Kochetov -// Created: 2009.05.06 - -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using Xtensive.Core; -using Xtensive.Orm.Linq.Expressions.Visitors; -using Xtensive.Orm.Linq.Materialization; -using Xtensive.Orm.Linq.Rewriters; -using Xtensive.Orm.Rse; -using Xtensive.Orm.Rse.Providers; -using Tuple = Xtensive.Tuples.Tuple; - -namespace Xtensive.Orm.Linq.Expressions -{ - internal class ItemProjectorExpression : ExtendedExpression - { - public CompilableProvider DataSource { get; set; } - public TranslatorContext Context { get; private set; } - public Expression Item { get; private set; } - - public bool IsPrimitive { get { return CheckItemIsPrimitive(Item); } } - - private bool CheckItemIsPrimitive(Expression item) - { - var extendedItem = item.StripCasts() as ExtendedExpression; - if (extendedItem==null) - return false; - switch (extendedItem.ExtendedType) { - case ExtendedExpressionType.Column: - case ExtendedExpressionType.Field: - return true; - case ExtendedExpressionType.Marker: - var marker = (MarkerExpression) extendedItem; - return CheckItemIsPrimitive(marker.Target); - default: - return false; - } - } - - public List GetColumns(ColumnExtractionModes columnExtractionModes) - { - return ColumnGatherer.GetColumns(Item, columnExtractionModes); - } - - public List> GetColumnsAndExpressions(ColumnExtractionModes columnExtractionModes) - { - return ColumnGatherer.GetColumnsAndExpressions(Item, columnExtractionModes); - } - - public ItemProjectorExpression Remap(CompilableProvider dataSource, int offset) - { - if (offset==0) - return new ItemProjectorExpression(Item, dataSource, Context); - var item = GenericExpressionVisitor.Process(Item, mapped => mapped.Remap(offset, new Dictionary())); - return new ItemProjectorExpression(item, dataSource, Context); - } - - public ItemProjectorExpression Remap(CompilableProvider dataSource, int[] columnMap) - { - var item = GenericExpressionVisitor.Process(Item, mapped => mapped.Remap(columnMap, new Dictionary())); - return new ItemProjectorExpression(item, dataSource, Context); - } - - public LambdaExpression ToLambda(TranslatorContext context) - { - return ExpressionMaterializer.MakeLambda(Item, context); - } - - public MaterializationInfo Materialize(TranslatorContext context, IEnumerable> tupleParameters) - { - return ExpressionMaterializer.MakeMaterialization(this, context, tupleParameters); - } - - public ItemProjectorExpression BindOuterParameter(ParameterExpression parameter) - { - var item = GenericExpressionVisitor.Process(Item, mapped => mapped.BindParameter(parameter, new Dictionary())); - return new ItemProjectorExpression(item, DataSource, Context); - } - - public ItemProjectorExpression RemoveOuterParameter() - { - var item = GenericExpressionVisitor.Process(Item, mapped => mapped.RemoveOuterParameter(new Dictionary())); - return new ItemProjectorExpression(item, DataSource, Context); - } - - public ItemProjectorExpression RemoveOwner() - { - var item = OwnerRemover.RemoveOwner(Item); - return new ItemProjectorExpression(item, DataSource, Context); - } - - public ItemProjectorExpression SetDefaultIfEmpty() - { - var item = GenericExpressionVisitor.Process(Item, mapped => { - mapped.DefaultIfEmpty = true; - return mapped; - }); - return new ItemProjectorExpression(item, DataSource, Context); - } - - public ItemProjectorExpression RewriteApplyParameter(ApplyParameter oldParameter, ApplyParameter newParameter) - { - var newDataSource = ApplyParameterRewriter.Rewrite(DataSource, oldParameter, newParameter); - var newItemProjectorBody = ApplyParameterRewriter.Rewrite(Item, oldParameter, newParameter); - return new ItemProjectorExpression(newItemProjectorBody, newDataSource, Context); - } - - public ItemProjectorExpression EnsureEntityIsJoined() - { - var dataSource = DataSource; - var newItem = new ExtendedExpressionReplacer(e => { - if (e is EntityExpression) { - var entityExpression = (EntityExpression) e; - var typeInfo = entityExpression.PersistentType; - - // Converted from LINQ to get rid of 2 closure allocations - var all = true; - foreach (var fieldInfo in typeInfo.Fields) { - var isUsedInEntityExpression = false; - foreach (var entityField in entityExpression.Fields) { - if (string.Equals(entityField.Name, fieldInfo.Name, StringComparison.Ordinal)) { - isUsedInEntityExpression = true; - break; - } - } - if (!isUsedInEntityExpression) { - all = false; - break; - } - } - if (all) - return entityExpression; - - var joinedIndex = typeInfo.Indexes.PrimaryIndex; - var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); - var keySegment = entityExpression.Key.Mapping; - var keyPairs = new Pair[keySegment.Length]; - var rightIndex = 0; - foreach (var leftIndex in keySegment.GetItems()) { - keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); - rightIndex++; - } - var offset = dataSource.Header.Length; - var dataSourceAsJoin = dataSource as JoinProvider; - dataSource = entityExpression.IsNullable || (dataSourceAsJoin!=null && dataSourceAsJoin.JoinType==JoinType.LeftOuter) - ? dataSource.LeftJoin(joinedRs, keyPairs) - : dataSource.Join(joinedRs, keyPairs); - EntityExpression.Fill(entityExpression, offset); - return entityExpression; - } - if (e is EntityFieldExpression) { - var entityFieldExpression = (EntityFieldExpression)e; - if (entityFieldExpression.Entity != null) - return entityFieldExpression.Entity; - var typeInfo = entityFieldExpression.PersistentType; - var joinedIndex = typeInfo.Indexes.PrimaryIndex; - var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); - var keySegment = entityFieldExpression.Mapping; - var keyPairs = new Pair[keySegment.Length]; - var rightIndex = 0; - foreach (var leftIndex in keySegment.GetItems()) { - keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); - rightIndex++; - } - var offset = dataSource.Header.Length; - var dataSourceAsJoin = dataSource as JoinProvider; - dataSource = entityFieldExpression.IsNullable || (dataSourceAsJoin!=null && dataSourceAsJoin.JoinType==JoinType.LeftOuter) - ? dataSource.LeftJoin(joinedRs, keyPairs) - : dataSource.Join(joinedRs, keyPairs); - entityFieldExpression.RegisterEntityExpression(offset); - return entityFieldExpression.Entity; - } - if (e is FieldExpression) { - var fe = (FieldExpression) e; - if (fe.ExtendedType==ExtendedExpressionType.Field) - return fe.RemoveOwner(); - } - return null; - }) - .Replace(Item); - return new ItemProjectorExpression(newItem, dataSource, Context); - } - - public override string ToString() - { - return string.Format("ItemProjectorExpression: IsPrimitive = {0} Item = {1}, DataSource = {2}", IsPrimitive, Item, DataSource); - } - - - // Constructors - - public ItemProjectorExpression(Expression expression, CompilableProvider dataSource, TranslatorContext context) - : base(ExtendedExpressionType.ItemProjector, expression.Type) - { - DataSource = dataSource; - Context = context; - var newApplyParameter = Context.GetApplyParameter(dataSource); - var applyParameterReplacer = new ExtendedExpressionReplacer(ex => - ex is SubQueryExpression - ? ((SubQueryExpression) ex).ReplaceApplyParameter(newApplyParameter) - : null); - Item = applyParameterReplacer.Replace(expression); - } - } -} +// Copyright (C) 2009-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: Alexis Kochetov +// Created: 2009.05.06 + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Xtensive.Core; +using Xtensive.Orm.Linq.Expressions.Visitors; +using Xtensive.Orm.Linq.Materialization; +using Xtensive.Orm.Linq.Rewriters; +using Xtensive.Orm.Rse; +using Xtensive.Orm.Rse.Providers; +using Tuple = Xtensive.Tuples.Tuple; + +namespace Xtensive.Orm.Linq.Expressions +{ + internal class ItemProjectorExpression : ExtendedExpression + { + public CompilableProvider DataSource { get; set; } + public TranslatorContext Context { get; } + public Expression Item { get; } + + public bool IsPrimitive => CheckItemIsPrimitive(Item); + + private static bool CheckItemIsPrimitive(Expression item) + { + if (!(item.StripCasts() is ExtendedExpression extendedItem)) { + return false; + } + + switch (extendedItem.ExtendedType) { + case ExtendedExpressionType.Column: + case ExtendedExpressionType.Field: + return true; + case ExtendedExpressionType.Marker: + var marker = (MarkerExpression) extendedItem; + return CheckItemIsPrimitive(marker.Target); + default: + return false; + } + } + + public List GetColumns(ColumnExtractionModes columnExtractionModes) => + ColumnGatherer.GetColumns(Item, columnExtractionModes); + + public List> GetColumnsAndExpressions(ColumnExtractionModes columnExtractionModes) => + ColumnGatherer.GetColumnsAndExpressions(Item, columnExtractionModes); + + public ItemProjectorExpression Remap(CompilableProvider dataSource, int offset) + { + if (offset == 0) { + return new ItemProjectorExpression(Item, dataSource, Context); + } + + var item = GenericExpressionVisitor + .Process(Item, mapped => mapped.Remap(offset, new Dictionary())); + return new ItemProjectorExpression(item, dataSource, Context); + } + + public ItemProjectorExpression Remap(CompilableProvider dataSource, int[] columnMap) + { + var item = GenericExpressionVisitor + .Process(Item, mapped => mapped.Remap(columnMap, new Dictionary())); + return new ItemProjectorExpression(item, dataSource, Context); + } + + public LambdaExpression ToLambda(TranslatorContext context) => ExpressionMaterializer.MakeLambda(Item, context); + + public MaterializationInfo Materialize(TranslatorContext context, IEnumerable> tupleParameters) => + ExpressionMaterializer.MakeMaterialization(this, context, tupleParameters); + + public ItemProjectorExpression BindOuterParameter(ParameterExpression parameter) + { + var item = GenericExpressionVisitor + .Process(Item, mapped => mapped.BindParameter(parameter, new Dictionary())); + return new ItemProjectorExpression(item, DataSource, Context); + } + + public ItemProjectorExpression RemoveOuterParameter() + { + var item = GenericExpressionVisitor + .Process(Item, mapped => mapped.RemoveOuterParameter(new Dictionary())); + return new ItemProjectorExpression(item, DataSource, Context); + } + + public ItemProjectorExpression RemoveOwner() + { + var item = OwnerRemover.RemoveOwner(Item); + return new ItemProjectorExpression(item, DataSource, Context); + } + + public ItemProjectorExpression SetDefaultIfEmpty() + { + var item = GenericExpressionVisitor.Process(Item, mapped => { + mapped.DefaultIfEmpty = true; + return mapped; + }); + return new ItemProjectorExpression(item, DataSource, Context); + } + + public ItemProjectorExpression RewriteApplyParameter(ApplyParameter oldParameter, ApplyParameter newParameter) + { + var newDataSource = ApplyParameterRewriter.Rewrite(DataSource, oldParameter, newParameter); + var newItemProjectorBody = ApplyParameterRewriter.Rewrite(Item, oldParameter, newParameter); + return new ItemProjectorExpression(newItemProjectorBody, newDataSource, Context); + } + + public ItemProjectorExpression EnsureEntityIsJoined() + { + var dataSource = DataSource; + var newItem = new ExtendedExpressionReplacer(e => { + if (e is EntityExpression entityExpression) { + var typeInfo = entityExpression.PersistentType; + + // Converted from LINQ to get rid of 2 closure allocations + var all = true; + foreach (var fieldInfo in typeInfo.Fields) { + var isUsedInEntityExpression = false; + foreach (var entityField in entityExpression.Fields) { + if (string.Equals(entityField.Name, fieldInfo.Name, StringComparison.Ordinal)) { + isUsedInEntityExpression = true; + break; + } + } + if (!isUsedInEntityExpression) { + all = false; + break; + } + } + + if (all) { + return entityExpression; + } + + var joinedIndex = typeInfo.Indexes.PrimaryIndex; + var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); + var keySegment = entityExpression.Key.Mapping; + var keyPairs = new Pair[keySegment.Length]; + var rightIndex = 0; + foreach (var leftIndex in keySegment.GetItems()) { + keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); + rightIndex++; + } + var offset = dataSource.Header.Length; + dataSource = entityExpression.IsNullable + || (dataSource is JoinProvider dataSourceAsJoin && dataSourceAsJoin.JoinType == JoinType.LeftOuter) + ? dataSource.LeftJoin(joinedRs, keyPairs) + : dataSource.Join(joinedRs, keyPairs); + EntityExpression.Fill(entityExpression, offset); + return entityExpression; + } + + if (e is EntityFieldExpression entityFieldExpression) { + if (entityFieldExpression.Entity != null) { + return entityFieldExpression.Entity; + } + + var typeInfo = entityFieldExpression.PersistentType; + var joinedIndex = typeInfo.Indexes.PrimaryIndex; + var joinedRs = joinedIndex.GetQuery().Alias(Context.GetNextAlias()); + var keySegment = entityFieldExpression.Mapping; + var keyPairs = new Pair[keySegment.Length]; + var rightIndex = 0; + foreach (var leftIndex in keySegment.GetItems()) { + keyPairs[rightIndex] = new Pair(leftIndex, rightIndex); + rightIndex++; + } + var offset = dataSource.Header.Length; + dataSource = entityFieldExpression.IsNullable + || (dataSource is JoinProvider dataSourceAsJoin && dataSourceAsJoin.JoinType == JoinType.LeftOuter) + ? dataSource.LeftJoin(joinedRs, keyPairs) + : dataSource.Join(joinedRs, keyPairs); + entityFieldExpression.RegisterEntityExpression(offset); + return entityFieldExpression.Entity; + } + + if (e is FieldExpression fe && fe.ExtendedType == ExtendedExpressionType.Field) { + return fe.RemoveOwner(); + } + + return null; + }) + .Replace(Item); + return new ItemProjectorExpression(newItem, dataSource, Context); + } + + public override string ToString() => + $"ItemProjectorExpression: IsPrimitive = {IsPrimitive} Item = {Item}, DataSource = {DataSource}"; + + + // Constructors + + public ItemProjectorExpression(Expression expression, CompilableProvider dataSource, TranslatorContext context) + : base(ExtendedExpressionType.ItemProjector, expression.Type) + { + DataSource = dataSource; + Context = context; + var newApplyParameter = Context.GetApplyParameter(dataSource); + var applyParameterReplacer = new ExtendedExpressionReplacer(ex => + ex is SubQueryExpression queryExpression + ? queryExpression.ReplaceApplyParameter(newApplyParameter) + : null); + Item = applyParameterReplacer.Replace(expression); + } + } +} diff --git a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs index e83d70f0d7..7b74dfb703 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureExpression.cs @@ -1,6 +1,6 @@ -// Copyright (C) 2003-2010 Xtensive LLC. -// All rights reserved. -// For conditions of distribution and use, see license. +// Copyright (C) 2009-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: Alexey Gamzov // Created: 2009.09.29 @@ -53,6 +53,7 @@ public Expression Remap(int offset, Dictionary processed // Do not convert to LINQ. We intentionally avoiding closure creation here processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); } + result.Fields = processedFields; result.isNullable = isNullable; return result; @@ -111,6 +112,7 @@ public Expression BindParameter(ParameterExpression parameter, Dictionary // Do not convert to LINQ. We want to avoid a closure creation here. processedFields.Add((PersistentFieldExpression) field.Remap(offset, processedExpressions)); } + if (Owner == null) { result.fields = processedFields; return result; @@ -98,6 +99,7 @@ public override Expression Remap(int[] map, Dictionary p result.fields = processedFields; return result; } + result.Fields = processedFields; Owner.Remap(map, processedExpressions); return result; @@ -153,7 +155,7 @@ public override Expression RemoveOuterParameter(Dictionary