Skip to content

Commit 2801df9

Browse files
authored
Merge branch 'master' into upstream/optimize_ProjectionExpression
2 parents 5b55c08 + 9498343 commit 2801df9

26 files changed

Lines changed: 360 additions & 325 deletions

Extensions/Xtensive.Orm.BulkOperations/Internals/Operation.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@ public async Task<int> ExecuteAsync(CancellationToken token = default)
5050
return value;
5151
}
5252

53-
protected void EnsureTransactionIsStarted() => Transaction.Require(QueryProvider.Session);
53+
protected void EnsureTransactionIsStarted()
54+
{
55+
Transaction.Require(QueryProvider.Session);
56+
#pragma warning disable 168
57+
// this prepares connection which ensures that connection is opened
58+
// this is weird way but it is required for some scenarios.
59+
_ = QueryProvider.Session.Services.Demand<DirectSqlAccessor>().Transaction;
60+
#pragma warning restore 168
61+
}
5462

5563
protected abstract int ExecuteInternal();
5664

Orm/Xtensive.Orm/Linq/ConstantExtractor.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
@@ -21,9 +21,10 @@ namespace Xtensive.Linq
2121
/// </summary>
2222
public sealed class ConstantExtractor : ExpressionVisitor
2323
{
24+
private static readonly ParameterExpression ConstantParameter = Expression.Parameter(WellKnownTypes.ObjectArray, "constants");
25+
2426
private readonly Func<ConstantExpression, bool> constantFilter;
2527
private readonly LambdaExpression lambda;
26-
private readonly ParameterExpression constantParameter;
2728
private List<object> constantValues;
2829

2930
/// <summary>
@@ -48,7 +49,7 @@ public LambdaExpression Process()
4849
if (constantValues != null)
4950
throw new InvalidOperationException();
5051
constantValues = new List<object>();
51-
var parameters = EnumerableUtils.One(constantParameter).Concat(lambda.Parameters).ToArray();
52+
var parameters = EnumerableUtils.One(ConstantParameter).Concat(lambda.Parameters).ToArray();
5253
var body = Visit(lambda.Body);
5354
// Preserve original delegate type because it may differ from types of parameters / return value
5455
return FastExpression.Lambda(FixDelegateType(lambda.Type), body, parameters);
@@ -60,7 +61,7 @@ protected override Expression VisitConstant(ConstantExpression c)
6061
if (!constantFilter.Invoke(c))
6162
return c;
6263
var result = Expression.Convert(
63-
Expression.ArrayIndex(constantParameter, Expression.Constant(constantValues.Count)), c.Type);
64+
Expression.ArrayIndex(ConstantParameter, Expression.Constant(constantValues.Count)), c.Type);
6465
constantValues.Add(c.Value);
6566
return result;
6667
}
@@ -70,7 +71,7 @@ protected override Expression VisitConstant(ConstantExpression c)
7071
private Type FixDelegateType(Type delegateType)
7172
{
7273
var signature = DelegateHelper.GetDelegateSignature(delegateType);
73-
return DelegateHelper.MakeDelegateType(signature.First, signature.Second.Prepend(constantParameter.Type));
74+
return DelegateHelper.MakeDelegateType(signature.First, signature.Second.Prepend(ConstantParameter.Type));
7475
}
7576

7677
private static bool DefaultConstantFilter(ConstantExpression constant)
@@ -106,7 +107,6 @@ public ConstantExtractor(LambdaExpression lambda, Func<ConstantExpression, bool>
106107
ArgumentValidator.EnsureArgumentNotNull(lambda, "lambda");
107108
this.lambda = lambda;
108109
this.constantFilter = constantFilter ?? DefaultConstantFilter;
109-
constantParameter = Expression.Parameter(WellKnownTypes.ObjectArray, "constants");
110110
}
111111
}
112112
}

Orm/Xtensive.Orm/Modelling/Comparison/Comparer.cs

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Xtensive.Reflection;
1515
using Xtensive.Modelling.Attributes;
1616
using Xtensive.Modelling.Comparison.Hints;
17+
using Xtensive.Orm;
1718

1819

1920
namespace Xtensive.Modelling.Comparison
@@ -149,21 +150,27 @@ protected virtual Difference VisitNode(Node source, Node target)
149150
{
150151
using (TryActivate(source, target, (s, t) => new NodeDifference(s, t))) {
151152
IgnoreHint ignoreHint = null;
152-
if (source!=null)
153+
if (source!=null) {
153154
ignoreHint = Hints.GetHint<IgnoreHint>(source);
154-
if (ignoreHint!=null)
155+
}
156+
157+
if (ignoreHint!=null) {
155158
return null;
159+
}
156160

157161
var context = Context;
158162
var difference = (NodeDifference) context.Difference;
159-
if (difference==null)
163+
if (difference==null) {
160164
throw new NullReferenceException();
165+
}
166+
161167
var any = source ?? target;
162-
if (any==null)
168+
if (any==null) {
163169
throw Exceptions.InternalError(Strings.ExBothSourceAndTargetAreNull, CoreLog.Instance);
170+
}
164171

165172

166-
bool isNewDifference = TryRegisterDifference(source, target, difference);
173+
var isNewDifference = TryRegisterDifference(source, target, difference);
167174
if (isNewDifference) {
168175
// Build movement info
169176
difference.MovementInfo = BuildMovementInfo(source, target);
@@ -353,68 +360,74 @@ protected IEnumerable<NodeDifference> GetPropertyDifferences(NodeDifference diff
353360
/// <exception cref="NullReferenceException">Current difference is not <see cref="NodeCollectionDifference"/>.</exception>
354361
protected virtual Difference VisitNodeCollection(NodeCollection source, NodeCollection target)
355362
{
356-
using (TryActivate(source, target, (s,t) => new NodeCollectionDifference(s,t))) {
363+
using (TryActivate(source, target, (s, t) => new NodeCollectionDifference(s, t))) {
357364
var context = Context;
358365
var difference = (NodeCollectionDifference) context.Difference;
359-
if (difference==null)
366+
if (difference == null) {
360367
throw new NullReferenceException();
368+
}
361369

362-
bool isNewDifference = TryRegisterDifference(source, target, difference);
370+
TryRegisterDifference(source, target, difference);
363371
difference.ItemChanges.Clear();
364372

365-
// Inlining 2 below lines leads to error in PEVerify.exe!
366-
// (well-known issue with null coalescing operator + cast)
367-
var sourceItems = (IEnumerable) source;
368-
var targetItems = (IEnumerable) target;
373+
if (source?.Count == 0 && target?.Count == 0) {
374+
return null;
375+
}
376+
377+
var sourceSize = source?.Count ?? 0;
378+
var sourceKeyMap = new Dictionary<string, Node>(sourceSize, StringComparer.OrdinalIgnoreCase);
379+
for (var index = sourceSize; index-- > 0;) {
380+
var node = source[index];
381+
sourceKeyMap.Add(GetNodeComparisonKey(node), node);
382+
}
369383

370-
var src = sourceItems ?? new ReadOnlyList<Node>(new Node[] {});
371-
var tgt = targetItems ?? new ReadOnlyList<Node>(new Node[] {});
384+
var targetSize = target?.Count ?? 0;
385+
var targetKeyMap = new Dictionary<string, Node>(targetSize, StringComparer.OrdinalIgnoreCase);
386+
for (var index = targetSize; index-- > 0;) {
387+
var node = target[index];
388+
targetKeyMap.Add(GetNodeComparisonKey(node), node);
389+
}
372390

373-
var srcCount = source!=null ? source.Count : 0;
374-
var tgtCount = target!=null ? target.Count : 0;
391+
foreach (var sourceItem in sourceKeyMap) {
392+
if (!targetKeyMap.ContainsKey(sourceItem.Key)) {
393+
var d = Visit(sourceKeyMap[sourceItem.Key], null);
394+
if (d != null) {
395+
difference.ItemChanges.Add((NodeDifference) d);
396+
}
397+
}
398+
}
375399

376-
if (srcCount==0 && tgtCount==0)
377-
return null;
378-
var someItems = srcCount!=0 ? src : tgt;
379-
var someItem = someItems.Cast<Node>().First();
380-
381-
Func<Node, Pair<Node, string>> keyExtractor =
382-
n => new Pair<Node, string>(n, GetNodeComparisonKey(n));
383-
384-
var sourceKeyMap = src
385-
.Cast<Node>()
386-
.Select(keyExtractor)
387-
.ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase);
388-
var targetKeyMap = tgt
389-
.Cast<Node>()
390-
.Select(keyExtractor)
391-
.ToDictionary(pair => pair.Second, pair => pair.First, StringComparer.OrdinalIgnoreCase);
392-
393-
var sourceKeys = src.Cast<Node>().Select(n => keyExtractor(n).Second);
394-
var targetKeys = tgt.Cast<Node>().Select(n => keyExtractor(n).Second);
395-
var commonKeys = sourceKeys.Intersect(targetKeys, StringComparer.OrdinalIgnoreCase);
396-
397-
var sequence =
398-
sourceKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase)
399-
.Select(k => new {Index = sourceKeyMap[k].Index, Type = 0,
400-
Source = sourceKeyMap[k], Target = (Node) null})
401-
.Concat(commonKeys
402-
.Select(k => new {Index = targetKeyMap[k].Index, Type = 1,
403-
Source = sourceKeyMap[k], Target = targetKeyMap[k]}))
404-
.Concat(targetKeys.Except(commonKeys, StringComparer.OrdinalIgnoreCase)
405-
.Select(k => new {Index = targetKeyMap[k].Index, Type = 2,
406-
Source = (Node) null, Target = targetKeyMap[k]}))
407-
.OrderBy(i => i.Type!=0).ThenBy(i => i.Index).ThenBy(i => i.Type);
408-
409-
foreach (var i in sequence) {
410-
var d = Visit(i.Source, i.Target);
411-
if (d!=null)
400+
foreach (var targetItem in targetKeyMap) {
401+
var (s, t) = sourceKeyMap.ContainsKey(targetItem.Key)
402+
? (sourceKeyMap[targetItem.Key], targetKeyMap[targetItem.Key])
403+
: (null, targetKeyMap[targetItem.Key]);
404+
var d = Visit(s, t);
405+
if (d != null) {
412406
difference.ItemChanges.Add((NodeDifference) d);
407+
}
408+
413409
}
410+
difference.ItemChanges.Sort(CompareNodeDifference);
414411

415-
return (difference.ItemChanges.Count!=0) ? difference : null;
412+
return difference.ItemChanges.Count != 0 ? difference : null;
416413
}
417414
}
415+
416+
// Sort by items only with source, then by (target ?? source).Index then with source and target and then only with target
417+
private static int CompareNodeDifference(NodeDifference curr, NodeDifference other)
418+
{
419+
var currType = curr.Source != null && curr.Target != null ? 1 : curr.Source == null ? 3 : 0;
420+
var otherType = other.Source != null && other.Target != null ? 1 : other.Source == null ? 3 : 0;
421+
var typeIsNot0Comparison = (currType != 0).CompareTo(otherType != 0);
422+
if (typeIsNot0Comparison != 0) {
423+
return typeIsNot0Comparison;
424+
}
425+
426+
var currIndex = (curr.Target ?? curr.Source)?.Index ?? 0;
427+
var otherIndex = (other.Target ?? other.Source)?.Index ?? 0;
428+
var indexComparison = currIndex.CompareTo(otherIndex);
429+
return indexComparison != 0 ? indexComparison : currType.CompareTo(otherType);
430+
}
418431

419432
/// <summary>
420433
/// Visits specified objects.

Orm/Xtensive.Orm/Modelling/Comparison/Upgrader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public ReadOnlyList<NodeAction> GetUpgradeSequence(Difference difference, HintSe
121121
ArgumentValidator.EnsureArgumentNotNull(hints, nameof(hints));
122122
ArgumentValidator.EnsureArgumentNotNull(comparer, nameof(comparer));
123123
if (difference == null) {
124-
return new ReadOnlyList<NodeAction>(Enumerable.Empty<NodeAction>().ToList());
124+
return ReadOnlyList<NodeAction>.Empty;
125125
}
126126

127127
TemporaryRenames = new Dictionary<string, Node>(StringComparer.OrdinalIgnoreCase);

Orm/Xtensive.Orm/Modelling/Node.cs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public abstract class Node : LockableBase,
3434
/// Path delimiter character.
3535
/// </summary>
3636
public static readonly char PathDelimiter = '/';
37+
public static readonly string PathDelimiterString = PathDelimiter.ToString();
3738
/// <summary>
3839
/// Path escape character.
3940
/// </summary>
@@ -185,18 +186,24 @@ public IEnumerable<Pair<string, IPathNode>> GetPathNodes(bool nestedOnly)
185186
public string Path {
186187
[DebuggerStepThrough]
187188
get {
188-
if (cachedPath!=null)
189+
if (cachedPath!=null) {
189190
return cachedPath;
190-
if (Parent==null)
191+
}
192+
193+
if (Parent==null) {
191194
return string.Empty;
192-
string parentPath = Parent.Path;
193-
if (parentPath.Length!=0)
194-
parentPath += PathDelimiter;
195+
}
196+
197+
var parentPath = Parent.Path;
198+
if (parentPath.Length!=0) {
199+
parentPath += PathDelimiterString;
200+
}
201+
195202
return string.Concat(
196203
parentPath,
197204
Nesting.EscapedPropertyName,
198-
Nesting.IsNestedToCollection ? PathDelimiter.ToString() : string.Empty,
199-
Nesting.IsNestedToCollection ? EscapedName : string.Empty);
205+
Nesting.IsNestedToCollection ? PathDelimiterString : null,
206+
Nesting.IsNestedToCollection ? EscapedName : null);
200207
}
201208
}
202209

@@ -972,7 +979,7 @@ public override string ToString()
972979
var m = Model;
973980
var fullName = Path;
974981
if (m != null) {
975-
fullName = string.Concat(m.EscapedName, PathDelimiter, fullName);
982+
fullName = string.Concat(m.EscapedName, PathDelimiterString, fullName);
976983
}
977984

978985
if (!Nesting.IsNestedToCollection && !(this is IModel)) {

Orm/Xtensive.Orm/Modelling/NodeCollection.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,13 @@
55
// Created: 2009.03.16
66

77
using System;
8-
using System.Linq;
98
using System.Collections;
109
using System.Collections.Generic;
1110
using System.Collections.Specialized;
1211
using System.Diagnostics;
1312
using System.Runtime.Serialization;
14-
using Xtensive.Collections;
1513
using Xtensive.Core;
1614

17-
using Xtensive.Modelling;
18-
using Xtensive.Modelling.Comparison;
19-
2015

2116
namespace Xtensive.Modelling
2217
{
@@ -28,8 +23,6 @@ public abstract class NodeCollection : LockableBase,
2823
INodeCollection,
2924
IDeserializationCallback
3025
{
31-
private static readonly ReadOnlyList<Node> emptyCountable =
32-
new ReadOnlyList<Node>(new List<Node>(), false);
3326
[NonSerialized]
3427
private string escapedName;
3528
[NonSerialized]
@@ -90,7 +83,7 @@ public string Path {
9083
if (parentPath.Length==0)
9184
return EscapedName;
9285
return string.Concat(
93-
parentPath, Node.PathDelimiter,
86+
parentPath, Node.PathDelimiterString,
9487
EscapedName);
9588
}
9689
}
@@ -355,7 +348,7 @@ public override string ToString()
355348
var m = Model;
356349
string fullName = Path;
357350
if (m!=null)
358-
fullName = string.Concat(m.EscapedName, Node.PathDelimiter, fullName);
351+
fullName = string.Concat(m.EscapedName, Node.PathDelimiterString, fullName);
359352
return string.Format(Strings.NodeInfoFormat, fullName, Count);
360353
}
361354

Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2020 Xtensive LLC.
1+
// Copyright (C) 2011-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
@@ -25,21 +25,21 @@ namespace Xtensive.Orm.Building.Builders
2525
{
2626
internal class PartialIndexFilterBuilder : ExpressionVisitor
2727
{
28+
private static readonly ParameterExpression Parameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
29+
2830
private readonly TypeInfo declaringType;
2931
private readonly TypeInfo reflectedType;
3032
private readonly IndexInfo index;
31-
private readonly ParameterExpression parameter;
3233
private readonly List<FieldInfo> usedFields = new List<FieldInfo>();
3334
private readonly Dictionary<Expression, FieldInfo> entityAccessMap = new Dictionary<Expression, FieldInfo>();
3435

3536
public static void BuildFilter(IndexInfo index)
3637
{
3738
ArgumentValidator.EnsureArgumentNotNull(index, "index");
38-
var parameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
39-
var builder = new PartialIndexFilterBuilder(index, parameter);
39+
var builder = new PartialIndexFilterBuilder(index);
4040
var body = builder.Visit(index.FilterExpression.Body);
4141
var filter = new PartialIndexFilterInfo {
42-
Expression = FastExpression.Lambda(body, parameter),
42+
Expression = FastExpression.Lambda(body, Parameter),
4343
Fields = builder.usedFields,
4444
};
4545
index.Filter = filter;
@@ -112,7 +112,7 @@ private Expression BuildFieldAccess(FieldInfo field, bool addNullability)
112112
var fieldIndex = usedFields.Count;
113113
var valueType = addNullability ? field.ValueType.ToNullable() : field.ValueType;
114114
usedFields.Add(field);
115-
return Expression.Call(parameter,
115+
return Expression.Call(Parameter,
116116
WellKnownMembers.Tuple.GenericAccessor.MakeGenericMethod(valueType),
117117
Expression.Constant(fieldIndex));
118118
}
@@ -174,10 +174,9 @@ private DomainBuilderException UnableToTranslate(Expression expression)
174174
return UnableToTranslate(expression, string.Format(Strings.ExpressionsOfTypeXAreNotSupported, expression.NodeType));
175175
}
176176

177-
private PartialIndexFilterBuilder(IndexInfo index, ParameterExpression parameter)
177+
private PartialIndexFilterBuilder(IndexInfo index)
178178
{
179179
this.index = index;
180-
this.parameter = parameter;
181180

182181
declaringType = index.DeclaringType;
183182
reflectedType = index.ReflectedType;

0 commit comments

Comments
 (0)