Skip to content

Commit e64fafa

Browse files
committed
Make projection remap in case of query reuse hapens
1 parent 0f4b696 commit e64fafa

File tree

3 files changed

+124
-58
lines changed

3 files changed

+124
-58
lines changed

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -310,34 +310,42 @@ protected override Expression VisitParameter(ParameterExpression p)
310310

311311
protected override Expression VisitMemberAccess(MemberExpression ma)
312312
{
313-
if (ma.Expression != null)
314-
if (ma.Expression.Type!=ma.Member.ReflectedType
315-
&& ma.Member is PropertyInfo
316-
&& !ma.Member.ReflectedType.IsInterface)
313+
var memberInfo = ma.Member;
314+
var sourceExpression = ma.Expression;
315+
316+
if (sourceExpression != null) {
317+
if (sourceExpression.Type != memberInfo.ReflectedType
318+
&& memberInfo is PropertyInfo
319+
&& !memberInfo.ReflectedType.IsInterface) {
317320
ma = Expression.MakeMemberAccess(
318-
ma.Expression, ma.Expression.Type.GetProperty(ma.Member.Name, ma.Member.GetBindingFlags()));
319-
var customCompiler = context.CustomCompilerProvider.GetCompiler(ma.Member);
321+
sourceExpression, sourceExpression.Type.GetProperty(memberInfo.Name, memberInfo.GetBindingFlags()));
322+
323+
memberInfo = ma.Member;
324+
sourceExpression = ma.Expression;
325+
}
326+
}
327+
328+
var customCompiler = context.CustomCompilerProvider.GetCompiler(memberInfo);
320329

321330
// Reflected type doesn't have custom compiler defined, so falling back to base class compiler
322-
var declaringType = ma.Member.DeclaringType;
323-
Type reflectedType = ma.Member.ReflectedType;
331+
var declaringType = memberInfo.DeclaringType;
332+
var reflectedType = memberInfo.ReflectedType;
324333
if (customCompiler == null && declaringType != reflectedType && declaringType.IsAssignableFrom(reflectedType)) {
325334
var root = declaringType;
326335
var current = reflectedType;
327336
while (current != root && customCompiler == null) {
328337
current = current.BaseType;
329-
var member = current.GetProperty(ma.Member.Name, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
338+
var member = current.GetProperty(memberInfo.Name, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
330339
customCompiler = context.CustomCompilerProvider.GetCompiler(member);
331340
}
332341
}
333342

334-
if (customCompiler!=null) {
335-
var member = ma.Member;
336-
var expression = customCompiler.Invoke(ma.Expression, ArrayUtils<Expression>.EmptyArray);
343+
if (customCompiler != null) {
344+
var expression = customCompiler.Invoke(sourceExpression, ArrayUtils<Expression>.EmptyArray);
337345
if (expression == null) {
338-
if (member.ReflectedType.IsInterface)
346+
if (reflectedType.IsInterface)
339347
return Visit(BuildInterfaceExpression(ma));
340-
if (member.ReflectedType.IsClass)
348+
if (reflectedType.IsClass)
341349
return Visit(BuildHierarchyExpression(ma));
342350
}
343351
else
@@ -346,47 +354,49 @@ protected override Expression VisitMemberAccess(MemberExpression ma)
346354

347355
if (context.Evaluator.CanBeEvaluated(ma) && context.ParameterExtractor.IsParameter(ma)) {
348356
if (typeof (IQueryable).IsAssignableFrom(ma.Type)) {
349-
Func<IQueryable> lambda = FastExpression.Lambda<Func<IQueryable>>(ma).CachingCompile();
350-
IQueryable rootPoint = lambda();
357+
var lambda = FastExpression.Lambda<Func<IQueryable>>(ma).CachingCompile();
358+
var rootPoint = lambda();
351359
if (rootPoint!=null)
352360
return base.Visit(rootPoint.Expression);
353361
}
354362
return ma;
355363
}
356-
if (ma.Expression==null) {
357-
if (typeof (IQueryable).IsAssignableFrom(ma.Type)) {
364+
if (sourceExpression == null) {
365+
if (typeof(IQueryable).IsAssignableFrom(ma.Type)) {
358366
var lambda = FastExpression.Lambda<Func<IQueryable>>(ma).CachingCompile();
359367
var rootPoint = lambda();
360-
if (rootPoint!=null)
368+
if (rootPoint != null)
361369
return VisitSequence(rootPoint.Expression);
362370
}
363371
}
364-
else if (ma.Expression.NodeType==ExpressionType.Constant) {
365-
var rfi = ma.Member as FieldInfo;
366-
if (rfi!=null && (rfi.FieldType.IsGenericType && typeof (IQueryable).IsAssignableFrom(rfi.FieldType))) {
372+
else if (sourceExpression.NodeType == ExpressionType.Constant) {
373+
if (memberInfo is FieldInfo rfi && (rfi.FieldType.IsGenericType && typeof(IQueryable).IsAssignableFrom(rfi.FieldType))) {
367374
var lambda = FastExpression.Lambda<Func<IQueryable>>(ma).CachingCompile();
368375
var rootPoint = lambda();
369-
if (rootPoint!=null)
376+
if (rootPoint != null)
370377
return VisitSequence(rootPoint.Expression);
371378
}
372379
}
373-
else if (ma.Expression.GetMemberType() == MemberType.Entity && ma.Member.Name != "Key") {
374-
var type = ma.Expression.Type;
375-
var parameter = ma.Expression as ParameterExpression;
376-
if (parameter != null) {
380+
else if (sourceExpression.GetMemberType() == MemberType.Entity && memberInfo.Name != "Key") {
381+
var type = sourceExpression.Type;
382+
if (sourceExpression is ParameterExpression parameter) {
377383
var projection = context.Bindings[parameter];
378384
type = projection.ItemProjector.Item.Type;
379385
}
380-
if (!context.Model.Types[type].Fields.Contains(context.Domain.Handlers.NameBuilder.BuildFieldName((PropertyInfo) ma.Member)))
381-
throw new NotSupportedException(String.Format(Strings.ExFieldMustBePersistent, ma.ToString(true)));
386+
if (!context.Model.Types[type].Fields.Contains(context.Domain.Handlers.NameBuilder.BuildFieldName((PropertyInfo) memberInfo))) {
387+
throw new NotSupportedException(string.Format(Strings.ExFieldMustBePersistent, ma.ToString(true)));
388+
}
382389
}
383390
Expression source;
384391
using (state.CreateScope()) {
385392
// state.BuildingProjection = false;
386-
source = Visit(ma.Expression);
393+
source = Visit(sourceExpression);
387394
}
388395

389-
var result = GetMember(source, ma.Member, ma);
396+
var result = context.CheckIfQueryReusePossible(memberInfo)
397+
? GetMemberWithRemap(source, memberInfo, ma)
398+
: GetMember(source, memberInfo, ma);
399+
390400
return result ?? base.VisitMemberAccess(ma);
391401
}
392402

@@ -1407,6 +1417,59 @@ bool propertyFilter(PersistentFieldExpression f)
14071417
: result;
14081418
}
14091419

1420+
private Expression GetMemberWithRemap(Expression expression, MemberInfo memberInfo, Expression sourceExpression)
1421+
{
1422+
var original = GetMember(expression, memberInfo, sourceExpression);
1423+
if (original.IsSubqueryExpression()) {
1424+
var subquery = (SubQueryExpression) original;
1425+
var projectionExpression = subquery.ProjectionExpression;
1426+
var itemProjector = projectionExpression.ItemProjector;
1427+
var columnIndexes = itemProjector.GetColumns(ColumnExtractionModes.KeepSegment).ToArray();
1428+
1429+
var expReplacer = new ExtendedExpressionReplacer((e) => {
1430+
if (e is GroupingExpression ge && ge.SelectManyInfo.GroupByProjection != null) {
1431+
var geProjectionExpression = ge.ProjectionExpression;
1432+
var geItemProjector = geProjectionExpression.ItemProjector;
1433+
var columnIndexes = geItemProjector.GetColumns(ColumnExtractionModes.KeepSegment).ToArray();
1434+
1435+
var newProjectionExpression = new ProjectionExpression(geProjectionExpression.Type,
1436+
geItemProjector.Remap(geItemProjector.DataSource, columnIndexes),
1437+
geProjectionExpression.TupleParameterBindings);
1438+
1439+
var groupByProjection = ge.SelectManyInfo.GroupByProjection;
1440+
var groupByProjector = groupByProjection.ItemProjector;
1441+
var groupByColumnIndexes = groupByProjector.GetColumns(ColumnExtractionModes.KeepSegment).ToArray();
1442+
1443+
var newGroupByProjection = new ProjectionExpression(groupByProjection.Type,
1444+
groupByProjector.Remap(groupByProjector.DataSource, groupByColumnIndexes),
1445+
groupByProjection.TupleParameterBindings);
1446+
1447+
var result = new GroupingExpression(ge.Type,
1448+
ge.OuterParameter, ge.DefaultIfEmpty, newProjectionExpression, ge.ApplyParameter, ge.KeyExpression,
1449+
new GroupingExpression.SelectManyGroupingInfo(newGroupByProjection));
1450+
return result;
1451+
}
1452+
else
1453+
return null;
1454+
});
1455+
1456+
var newItem = expReplacer.Replace(itemProjector.Item);
1457+
var staysTheSame = newItem == itemProjector.Item;
1458+
var newItemProjector = staysTheSame
1459+
? itemProjector.Remap(itemProjector.DataSource, columnIndexes)
1460+
: new ItemProjectorExpression(newItem, itemProjector.DataSource, itemProjector.Context);
1461+
1462+
var result = new SubQueryExpression(subquery.Type,
1463+
subquery.OuterParameter,
1464+
subquery.DefaultIfEmpty,
1465+
new ProjectionExpression(projectionExpression.Type, newItemProjector, projectionExpression.TupleParameterBindings),
1466+
subquery.ApplyParameter,
1467+
subquery.ExtendedType);
1468+
return result;
1469+
}
1470+
return original;
1471+
}
1472+
14101473
private Expression GetConditionalMember(Expression expression, MemberInfo member, Expression sourceExpression)
14111474
{
14121475
var ce = expression as ConditionalExpression;

Orm/Xtensive.Orm/Orm/Linq/Translator.Materialization.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,17 @@ private Func<IEnumerable<Tuple>, Session, Dictionary<Parameter<Tuple>, Tuple>, P
151151
private List<Expression> VisitNewExpressionArguments(NewExpression n)
152152
{
153153
var arguments = new List<Expression>();
154-
foreach (var argument in n.Arguments) {
154+
var origArguments = n.Arguments;
155+
for (int i = 0, count = origArguments.Count; i < count; i++) {
156+
var argument = origArguments[i];
157+
155158
Expression body;
156159
using (state.CreateScope()) {
157160
state.CalculateExpressions = false;
158161
body = Visit(argument);
162+
if (argument.IsQuery()) {
163+
context.RegisterPossibleQueryReuse(n.Members[i]);
164+
}
159165
}
160166
body = body.IsProjection()
161167
? BuildSubqueryResult((ProjectionExpression) body, argument.Type)

Orm/Xtensive.Orm/Orm/Linq/TranslatorContext.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Collections.Generic;
88
using System.Linq;
99
using System.Linq.Expressions;
10+
using System.Reflection;
1011
using Xtensive.Core;
1112
using Xtensive.Orm.Linq.Expressions;
1213
using Xtensive.Orm.Linq.MemberCompilation;
@@ -28,6 +29,7 @@ internal sealed class TranslatorContext
2829
private readonly Dictionary<ParameterExpression, Parameter<Tuple>> tupleParameters;
2930
private readonly Dictionary<CompilableProvider, ApplyParameter> applyParameters;
3031
private readonly Dictionary<ParameterExpression, ItemProjectorExpression> boundItemProjectors;
32+
private readonly Dictionary<MemberInfo, int> queryReuses;
3133

3234
public CompilerConfiguration RseCompilerConfiguration { get; private set; }
3335

@@ -51,30 +53,17 @@ internal sealed class TranslatorContext
5153

5254
public LinqBindingCollection Bindings { get; private set; }
5355

54-
public bool IsRoot(Expression expression)
55-
{
56-
return Query==expression;
57-
}
56+
public bool IsRoot(Expression expression) => Query == expression;
5857

59-
public string GetNextAlias()
60-
{
61-
return resultAliasGenerator.Next();
62-
}
58+
public string GetNextAlias() => resultAliasGenerator.Next();
6359

64-
public string GetNextColumnAlias()
65-
{
66-
return columnAliasGenerator.Next();
67-
}
60+
public string GetNextColumnAlias() => columnAliasGenerator.Next();
6861

69-
public ApplyParameter GetApplyParameter(ProjectionExpression projection)
70-
{
71-
return GetApplyParameter(projection.ItemProjector.DataSource);
72-
}
62+
public ApplyParameter GetApplyParameter(ProjectionExpression projection) => GetApplyParameter(projection.ItemProjector.DataSource);
7363

7464
public ApplyParameter GetApplyParameter(CompilableProvider provider)
7565
{
76-
ApplyParameter parameter;
77-
if (!applyParameters.TryGetValue(provider, out parameter)) {
66+
if (!applyParameters.TryGetValue(provider, out var parameter)) {
7867
parameter = new ApplyParameter(provider.GetType().GetShortName());
7968
// parameter = new ApplyParameter(provider.ToString());
8069
// ENABLE ONLY FOR DEBUGGING!
@@ -86,16 +75,14 @@ public ApplyParameter GetApplyParameter(CompilableProvider provider)
8675

8776
public void RebindApplyParameter(CompilableProvider old, CompilableProvider @new)
8877
{
89-
ApplyParameter parameter;
90-
if (applyParameters.TryGetValue(old, out parameter)) {
78+
if (applyParameters.TryGetValue(old, out var parameter)) {
9179
applyParameters[@new] = parameter;
9280
}
9381
}
9482

9583
public Parameter<Tuple> GetTupleParameter(ParameterExpression expression)
9684
{
97-
Parameter<Tuple> parameter;
98-
if (!tupleParameters.TryGetValue(expression, out parameter)) {
85+
if (!tupleParameters.TryGetValue(expression, out var parameter)) {
9986
parameter = new Parameter<Tuple>(expression.ToString());
10087
tupleParameters.Add(expression, parameter);
10188
}
@@ -104,18 +91,27 @@ public Parameter<Tuple> GetTupleParameter(ParameterExpression expression)
10491

10592
public ItemProjectorExpression GetBoundItemProjector(ParameterExpression parameter, ItemProjectorExpression itemProjector)
10693
{
107-
ItemProjectorExpression result;
108-
if (!boundItemProjectors.TryGetValue(parameter, out result)) {
94+
if (!boundItemProjectors.TryGetValue(parameter, out var result)) {
10995
result = itemProjector.BindOuterParameter(parameter);
11096
boundItemProjectors.Add(parameter, result);
11197
}
11298
return result;
11399
}
114100

101+
public void RegisterPossibleQueryReuse(MemberInfo memberInfo) => queryReuses.Add(memberInfo, 0);
102+
103+
public bool CheckIfQueryReusePossible(MemberInfo memberInfo)
104+
{
105+
if (queryReuses.TryGetValue(memberInfo, out var uses)) {
106+
queryReuses[memberInfo] = uses + 1;
107+
return uses > 0;
108+
}
109+
return false;
110+
}
111+
115112
private Expression ApplyPreprocessor(IQueryPreprocessor preprocessor, Session session, Expression query)
116113
{
117-
var preprocessor2 = preprocessor as IQueryPreprocessor2;
118-
return preprocessor2!=null
114+
return preprocessor is IQueryPreprocessor2 preprocessor2
119115
? preprocessor2.Apply(session, query)
120116
: preprocessor.Apply(query);
121117
}
@@ -157,6 +153,7 @@ public TranslatorContext(Session session, CompilerConfiguration rseCompilerConfi
157153
applyParameters = new Dictionary<CompilableProvider, ApplyParameter>();
158154
tupleParameters = new Dictionary<ParameterExpression, Parameter<Tuple>>();
159155
boundItemProjectors = new Dictionary<ParameterExpression, ItemProjectorExpression>();
156+
queryReuses = new Dictionary<MemberInfo, int>();
160157
}
161158
}
162159
}

0 commit comments

Comments
 (0)