Skip to content

Commit 290a603

Browse files
committed
Merge branch '6.0' into 7.0
# Conflicts: # Orm/Xtensive.Orm.Tests/Linq/CustomExpressionCompilers.cs
2 parents 047c35e + 2eabbe7 commit 290a603

2 files changed

Lines changed: 94 additions & 35 deletions

File tree

Orm/Xtensive.Orm.Tests/Linq/CustomExpressionCompilers.cs

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
// Created: 2009.11.13
66

77
using System;
8+
using System.Linq;
89
using System.Linq.Expressions;
910
using NUnit.Framework;
1011
using Xtensive.Core;
1112
using Xtensive.Linq;
1213
using Xtensive.Orm.Configuration;
1314
using Xtensive.Orm.Tests.Linq.CustomExpressionCompilersModel;
14-
using System.Linq;
1515

1616
namespace Xtensive.Orm.Tests.Linq.CustomExpressionCompilersModel
1717
{
@@ -80,24 +80,21 @@ public int InstanceMethod(int value)
8080
return value * Id;
8181
}
8282
}
83-
}
8483

85-
namespace Xtensive.Orm.Tests.Linq
86-
{
87-
[CompilerContainer(typeof (Expression))]
84+
[CompilerContainer(typeof(Expression))]
8885
internal static class CustomLinqCompilerContainer
8986
{
90-
[Compiler(typeof (Person), "Fullname", TargetKind.PropertyGet)]
87+
[Compiler(typeof(Person), "Fullname", TargetKind.PropertyGet)]
9188
public static Expression FullName(Expression personExpression)
9289
{
9390
Expression<Func<Person, string>> ex = person => person.FirstName + " " + person.LastName;
9491
return ex.BindParameters(personExpression);
9592
}
9693

97-
[Compiler(typeof (Person), "AddPrefix", TargetKind.Method)]
94+
[Compiler(typeof(Person), "AddPrefix", TargetKind.Method)]
9895
public static Expression AddPrefix(Expression personExpression, Expression prefixExpression)
9996
{
100-
Expression<Func<Person, string, string>> ex = (person, prefix) => prefix + person.LastName;
97+
Expression<Func<Person, string, string>> ex = (person, prefix) => prefix + person.LastName;
10198
return ex.BindParameters(personExpression, prefixExpression);
10299
}
103100

@@ -108,22 +105,24 @@ public static Expression Current(Expression assignmentExpression)
108105
return ex.BindParameters(assignmentExpression);
109106
}
110107
}
108+
}
111109

110+
namespace Xtensive.Orm.Tests.Linq
111+
{
112112
public class CustomExpressionCompilers : AutoBuildTest
113113
{
114114
protected override DomainConfiguration BuildConfiguration()
115115
{
116116
var config = base.BuildConfiguration();
117117
config.Types.RegisterCaching(typeof (Person).Assembly, typeof (Person).Namespace);
118-
config.Types.Register(typeof (CustomLinqCompilerContainer));
119118
RegisterLinqExtensions(config);
120119
return config;
121120
}
122121

123122
private static void RegisterLinqExtensions(DomainConfiguration config)
124123
{
125124
var extensions = config.LinqExtensions;
126-
var type = typeof (RegistrarTestEntity);
125+
var type = typeof(RegistrarTestEntity);
127126
Expression<Func<int>> staticProperty = () => 0;
128127
extensions.Register(type.GetProperty("StaticProperty"), staticProperty);
129128
Expression<Func<RegistrarTestEntity, int>> instanceProperty = e => e.Id % 10;
@@ -137,35 +136,41 @@ private static void RegisterLinqExtensions(DomainConfiguration config)
137136
[Test]
138137
public void MainTest()
139138
{
140-
using (var session = Domain.OpenSession()) {
141-
using (var t = session.OpenTransaction()) {
142-
Fill();
143-
var expected1 = session.Query.All<Person>().AsEnumerable().OrderBy(p => p.Id).Select(p => p.Fullname).ToList();
144-
Assert.Greater(expected1.Count, 0);
145-
var fullNames1 = session.Query.All<Person>().OrderBy(p => p.Id).Select(p => p.Fullname).ToList();
146-
Assert.IsTrue(expected1.SequenceEqual(fullNames1));
147-
148-
var expected2 = session.Query.All<Person>().AsEnumerable().OrderBy(p => p.Id).Select(p => p.AddPrefix("Mr. ")).ToList();
149-
var fullNames2 = session.Query.All<Person>().OrderBy(p => p.Id).Select(p => p.AddPrefix("Mr. ")).ToList();
150-
Assert.IsTrue(expected2.SequenceEqual(fullNames2));
151-
// Rollback
152-
}
139+
using (var session = Domain.OpenSession())
140+
using (var t = session.OpenTransaction()) {
141+
Fill();
142+
var expected1 = session.Query.All<Person>().AsEnumerable().OrderBy(p => p.Id).Select(p => p.Fullname).ToList();
143+
Assert.Greater(expected1.Count, 0);
144+
var fullNames1 = session.Query.All<Person>().OrderBy(p => p.Id).Select(p => p.Fullname).ToList();
145+
Assert.IsTrue(expected1.SequenceEqual(fullNames1));
146+
147+
var expected2 = session.Query.All<Person>().AsEnumerable().OrderBy(p => p.Id).Select(p => p.AddPrefix("Mr. ")).ToList();
148+
var fullNames2 = session.Query.All<Person>().OrderBy(p => p.Id).Select(p => p.AddPrefix("Mr. ")).ToList();
149+
Assert.IsTrue(expected2.SequenceEqual(fullNames2));
150+
// Rollback
153151
}
154152
}
155153

156154
[Test]
157155
public void AssignmentCurrentTest()
158156
{
157+
var baseDate = DateTime.Today;
158+
(bool active, DateTime startDate, DateTime? endDate)[] dataset = new[] {
159+
(true, baseDate.AddYears(-20), (DateTime?)null),
160+
(false, baseDate.AddYears(-20), (DateTime?)null),
161+
(false, baseDate.AddYears(-5), baseDate.AddYears(20)),
162+
(true, baseDate.AddYears(1), baseDate.AddYears(20)),
163+
(true, baseDate.AddYears(-15), baseDate.AddYears(20)),
164+
};
165+
159166
using (var session = Domain.OpenSession())
160167
using (var t = session.OpenTransaction()) {
161-
new Assignment() {Active = true, Start = new DateTime(2009, 11, 23), End = null};
162-
new Assignment() {Active = false, Start = new DateTime(2009, 10, 3), End = null};
163-
new Assignment() {Active = false, Start = new DateTime(2020, 01, 10), End = new DateTime(2044, 12, 3)};
164-
new Assignment() {Active = true, Start = new DateTime(2026, 01, 10), End = new DateTime(2045, 11, 3)};
165-
new Assignment() {Active = true, Start = new DateTime(2010, 01, 10), End = new DateTime(2035, 11, 3)};
168+
foreach (var data in dataset) {
169+
_ = new Assignment() { Active = data.active, Start = data.startDate, End = data.endDate };
170+
}
166171

167172
var currentCount = session.Query.All<Assignment>().Count(a => a.Current);
168-
Assert.AreEqual(2, currentCount);
173+
Assert.That(currentCount, Is.EqualTo(2));
169174
// Rollback
170175
}
171176
}
@@ -197,9 +202,9 @@ public void RegistrarTest()
197202

198203
private void Fill()
199204
{
200-
new Person {FirstName = "Ivan", LastName = "Semenov"};
201-
new Person {FirstName = "John", LastName = "Smith"};
202-
new Person {FirstName = "Andrew", LastName = "Politkovsky"};
205+
_ = new Person { FirstName = "Ivan", LastName = "Semenov" };
206+
_ = new Person { FirstName = "John", LastName = "Smith" };
207+
_ = new Person { FirstName = "Andrew", LastName = "Politkovsky" };
203208
}
204209
}
205210
}

Orm/Xtensive.Orm/Linq/SerializableExpressions/Internals/SerializableExpressionToExpressionConverter.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,22 @@ namespace Xtensive.Linq.SerializableExpressions.Internals
1414
{
1515
internal sealed class SerializableExpressionToExpressionConverter
1616
{
17+
private readonly struct LambdaParameterScope : IDisposable
18+
{
19+
private readonly SerializableExpressionToExpressionConverter converter;
20+
21+
public void Dispose() => converter.parameterScopes.Pop();
22+
23+
public LambdaParameterScope(SerializableExpressionToExpressionConverter converter, Dictionary<string, ParameterExpression> currentScope)
24+
{
25+
this.converter = converter;
26+
this.converter.parameterScopes.Push(currentScope);
27+
}
28+
}
29+
1730
private readonly SerializableExpression source;
1831
private readonly Dictionary<SerializableExpression, Expression> cache;
32+
private readonly Stack<Dictionary<string, ParameterExpression>> parameterScopes;
1933

2034
public Expression Convert()
2135
{
@@ -137,12 +151,12 @@ private Expression VisitConstant(SerializableConstantExpression c)
137151

138152
private Expression VisitConditional(SerializableConditionalExpression c)
139153
{
140-
return Expression.Condition(Visit(c.Test), Visit(c.IfTrue), Visit(c.IfFalse));
154+
return Expression.Condition(Visit(c.Test), Visit(c.IfTrue), Visit(c.IfFalse), c.Type);
141155
}
142156

143157
private Expression VisitParameter(SerializableParameterExpression p)
144158
{
145-
return Expression.Parameter(p.Type, p.Name);
159+
return GetCachedParameter(p.Type, p.Name) ?? Expression.Parameter(p.Type, p.Name);
146160
}
147161

148162
private Expression VisitMemberAccess(SerializableMemberExpression m)
@@ -167,7 +181,10 @@ private Expression VisitMethodCall(SerializableMethodCallExpression mc)
167181

168182
private Expression VisitLambda(SerializableLambdaExpression l)
169183
{
170-
return FastExpression.Lambda(l.Type, Visit(l.Body), l.Parameters.Select(p => (ParameterExpression) Visit(p)));
184+
var parameters = l.Parameters.Select(p => (ParameterExpression) Visit(p)).ToList();
185+
using (CreateParameterScope(parameters)) {
186+
return FastExpression.Lambda(l.Type, Visit(l.Body), parameters);
187+
}
171188
}
172189

173190
private Expression VisitNew(SerializableNewExpression n)
@@ -238,12 +255,49 @@ private IEnumerable<Expression> VisitExpressionSequence<T>(IEnumerable<T> expres
238255
return expressions.Select(e => Visit(e));
239256
}
240257

258+
private LambdaParameterScope CreateParameterScope(IReadOnlyList<ParameterExpression> lambdaParameters)
259+
{
260+
var parameters = new Dictionary<string, ParameterExpression>(lambdaParameters.Count);
261+
foreach (var lambdaParameter in lambdaParameters) {
262+
parameters.Add(lambdaParameter.Name, lambdaParameter);
263+
}
264+
return new LambdaParameterScope(this, parameters);
265+
}
266+
267+
private ParameterExpression GetCachedParameter(Type type, string name)
268+
{
269+
var replacement = FindParameterFast(type, name);
270+
if (replacement == null && parameterScopes.Count > 1)
271+
return FindParameterSlow(type, name);
272+
return replacement;
273+
}
274+
275+
private ParameterExpression FindParameterFast(Type type, string name)
276+
{
277+
if (parameterScopes.Count > 0) {
278+
var currentParameters = parameterScopes.Peek();
279+
if (currentParameters.TryGetValue(name, out var replacement) && replacement.Type == type)
280+
return replacement;
281+
}
282+
return null;
283+
}
284+
285+
private ParameterExpression FindParameterSlow(Type type, string name)
286+
{
287+
foreach (var scope in parameterScopes) {
288+
if (scope.TryGetValue(name, out var replacement) && replacement.Type == type)
289+
return replacement;
290+
}
291+
return null;
292+
}
293+
241294
#endregion
242295

243296
public SerializableExpressionToExpressionConverter(SerializableExpression source)
244297
{
245298
this.source = source;
246299
cache = new Dictionary<SerializableExpression, Expression>();
300+
parameterScopes = new Stack<Dictionary<string, ParameterExpression>>();
247301
}
248302
}
249303
}

0 commit comments

Comments
 (0)