Skip to content

Commit a527b24

Browse files
Fix IncludeOptimized issue
Fix IncludeOptimized issue
1 parent e199af0 commit a527b24

File tree

3 files changed

+189
-10
lines changed

3 files changed

+189
-10
lines changed

src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/QueryIncludeOptimizedByPath.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
#if !EFCORE
2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Linq.Expressions;
@@ -16,17 +17,17 @@ public static class QueryIncludeOptimizedByPath
1617
/// <returns>An IQueryable&lt;T&gt;</returns>
1718
public static IQueryable<T> IncludeOptimizedByPath<T>(IQueryable<T> query, string navigationPath)
1819
{
19-
var elementType = typeof (T);
20+
var elementType = typeof(T);
2021
var paths = navigationPath.Split('.');
2122

2223
// CREATE expression x => x.Right
2324
var expression = CreateLambdaExpression(elementType, paths, 0);
2425

25-
var method = typeof (QueryIncludeOptimizedExtensions)
26+
var method = typeof(QueryIncludeOptimizedExtensions)
2627
.GetMethod("IncludeOptimizedSingle", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
27-
.MakeGenericMethod(typeof (T), expression.Type.GetGenericArguments()[1]);
28+
.MakeGenericMethod(typeof(T), expression.Type.GetGenericArguments()[1]);
2829

29-
query = (IQueryable<T>) method.Invoke(null, new object[] {query, expression});
30+
query = (IQueryable<T>)method.Invoke(null, new object[] { query, expression });
3031

3132
return query;
3233
}
@@ -48,18 +49,18 @@ public static Expression CreateLambdaExpression(Type parameterType, string[] pat
4849
expression = AppendPropertyPath(expression, paths, currentIndex);
4950

5051
// GET function generic type
51-
var funcGenericType = typeof (Func<,>).MakeGenericType(parameterType, expression.Type);
52+
var funcGenericType = typeof(Func<,>).MakeGenericType(parameterType, expression.Type);
5253

5354
// GET lambda method
54-
var lambdaMethod = typeof (Expression).GetMethods()
55+
var lambdaMethod = typeof(Expression).GetMethods()
5556
.Single(x => x.Name == "Lambda"
5657
&& x.IsGenericMethod
5758
&& x.GetParameters().Length == 2
5859
&& !x.GetParameters()[1].ParameterType.IsArray)
5960
.MakeGenericMethod(funcGenericType);
6061

6162
// CREATE lambda expression
62-
expression = (Expression) lambdaMethod.Invoke(null, new object[] {expression, new List<ParameterExpression> {parameter}});
63+
expression = (Expression)lambdaMethod.Invoke(null, new object[] { expression, new List<ParameterExpression> { parameter } });
6364

6465
return expression;
6566
}
@@ -134,7 +135,7 @@ public static Expression AppendSelectPath(Expression expression, string[] paths,
134135
var lambdaExpression = CreateLambdaExpression(elementType, paths, currentIndex);
135136

136137
// APPEND Method [.Select(y => y.Right)]
137-
var selectMethod = typeof (Enumerable).GetMethods()
138+
var selectMethod = typeof(Enumerable).GetMethods()
138139
.Single(x => x.Name == "Select"
139140
&& x.GetParameters().Length == 2
140141
&& x.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2)
@@ -144,4 +145,5 @@ public static Expression AppendSelectPath(Expression expression, string[] paths,
144145
return expression;
145146
}
146147
}
147-
}
148+
}
149+
#endif
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#if EFCORE
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
using System.Reflection;
7+
using Microsoft.EntityFrameworkCore;
8+
using Microsoft.EntityFrameworkCore.Metadata;
9+
10+
namespace Z.EntityFramework.Plus
11+
{
12+
/// <summary>A query include optimized by path.</summary>
13+
public static class QueryIncludeOptimizedByPath
14+
{
15+
/// <summary>Include optimized by path.</summary>
16+
/// <typeparam name="T">Generic type parameter.</typeparam>
17+
/// <param name="query">The query.</param>
18+
/// <param name="navigationPath">Full pathname of the navigation file.</param>
19+
/// <returns>An IQueryable&lt;T&gt;</returns>
20+
public static IQueryable<T> IncludeOptimizedByPath<T>(IQueryable<T> query, string navigationPath)
21+
{
22+
var elementType = typeof(T);
23+
var paths = navigationPath.Split('.');
24+
25+
var context = query.IsInMemoryQueryContext() ? null : query.GetDbContext();
26+
27+
// CREATE expression x => x.Right
28+
var expression = CreateLambdaExpression(elementType, paths, 0, context);
29+
30+
var method = typeof(QueryIncludeOptimizedExtensions)
31+
.GetMethod("IncludeOptimizedSingle", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
32+
.MakeGenericMethod(typeof(T), expression.Type.GetGenericArguments()[1]);
33+
34+
query = (IQueryable<T>)method.Invoke(null, new object[] { query, expression });
35+
36+
return query;
37+
}
38+
39+
/// <summary>Creates lambda expression.</summary>
40+
/// <param name="parameterType">Type of the parameter.</param>
41+
/// <param name="paths">The paths.</param>
42+
/// <param name="currentIndex">The current index.</param>
43+
/// <returns>The new lambda expression.</returns>
44+
public static Expression CreateLambdaExpression(Type parameterType, string[] paths, int currentIndex, DbContext context)
45+
{
46+
// CREATE expression [x => x.Right]
47+
48+
// ADD parameter [x =>]
49+
var parameter = Expression.Parameter(parameterType);
50+
Expression expression = parameter;
51+
52+
// ADD property [x.Right]
53+
expression = AppendPropertyPath(expression, paths, currentIndex, context);
54+
55+
// GET function generic type
56+
var funcGenericType = typeof(Func<,>).MakeGenericType(parameterType, expression.Type);
57+
58+
// GET lambda method
59+
var lambdaMethod = typeof(Expression).GetMethods()
60+
.Single(x => x.Name == "Lambda"
61+
&& x.IsGenericMethod
62+
&& x.GetParameters().Length == 2
63+
&& !x.GetParameters()[1].ParameterType.IsArray)
64+
.MakeGenericMethod(funcGenericType);
65+
66+
// CREATE lambda expression
67+
expression = (Expression)lambdaMethod.Invoke(null, new object[] { expression, new List<ParameterExpression> { parameter } });
68+
69+
return expression;
70+
}
71+
72+
/// <summary>Appends a path.</summary>
73+
/// <param name="expression">The expression.</param>
74+
/// <param name="paths">The paths.</param>
75+
/// <param name="currentIndex">The current index.</param>
76+
/// <returns>An Expression.</returns>
77+
public static Expression AppendPath(Expression expression, string[] paths, int currentIndex, DbContext context)
78+
{
79+
expression = expression.Type.GetGenericArguments().Length == 0 ?
80+
AppendPropertyPath(expression, paths, currentIndex, context) :
81+
AppendSelectPath(expression, paths, currentIndex, context);
82+
83+
return expression;
84+
}
85+
86+
/// <summary>Appends a property path.</summary>
87+
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
88+
/// <param name="expression">The expression.</param>
89+
/// <param name="paths">The paths.</param>
90+
/// <param name="currentIndex">The current index.</param>
91+
/// <returns>An Expression.</returns>
92+
public static Expression AppendPropertyPath(Expression expression, string[] paths, int currentIndex, DbContext context)
93+
{
94+
// APPEND [x.PropertyName]
95+
var elementType = expression.Type;
96+
var property = elementType.GetProperty(paths[currentIndex], BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
97+
98+
// ENSURE property exists
99+
if (property == null)
100+
{
101+
// Try Again with case insensitive
102+
var properties = elementType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
103+
.Where(x => x.Name.ToLowerInvariant() == paths[currentIndex].ToLowerInvariant()).ToList();
104+
105+
if (properties.Count == 1)
106+
{
107+
property = properties[0];
108+
}
109+
110+
// last try with GetDerivedTypes
111+
if (property == null && context != null)
112+
{
113+
var entityType = context.Model.FindEntityType(elementType);
114+
115+
foreach (var entity in entityType.GetDerivedTypes())
116+
{
117+
if (entity.ClrType != null)
118+
{
119+
properties = entity.ClrType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
120+
.Where(x => x.Name.ToLowerInvariant() == paths[currentIndex].ToLowerInvariant()).ToList();
121+
122+
if (properties.Count == 1)
123+
{
124+
property = properties[0];
125+
expression = Expression.Convert(expression, entity.ClrType);
126+
break;
127+
}
128+
}
129+
130+
}
131+
132+
if (property == null)
133+
{
134+
throw new Exception(string.Format(ExceptionMessage.QueryIncludeOptimized_ByPath_MissingPath, elementType.FullName, paths[currentIndex]));
135+
}
136+
}
137+
}
138+
139+
expression = Expression.Property(expression, property);
140+
141+
// APPEND path childs
142+
currentIndex++;
143+
if (currentIndex < paths.Length)
144+
{
145+
expression = AppendPath(expression, paths, currentIndex, context);
146+
}
147+
148+
return expression;
149+
}
150+
151+
/// <summary>Appends a select path.</summary>
152+
/// <param name="expression">The expression.</param>
153+
/// <param name="paths">The paths.</param>
154+
/// <param name="currentIndex">The current index.</param>
155+
/// <returns>An Expression.</returns>
156+
public static Expression AppendSelectPath(Expression expression, string[] paths, int currentIndex, DbContext context)
157+
{
158+
// APPEND x => x.Rights[.Select(y => y.Right)]
159+
var elementType = expression.Type.GetGenericArguments()[0];
160+
161+
// CREATE lambda expression [y => y.Right]
162+
var lambdaExpression = CreateLambdaExpression(elementType, paths, currentIndex, context);
163+
164+
// APPEND Method [.Select(y => y.Right)]
165+
var selectMethod = typeof(Enumerable).GetMethods()
166+
.Single(x => x.Name == "Select"
167+
&& x.GetParameters().Length == 2
168+
&& x.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2)
169+
.MakeGenericMethod(elementType, lambdaExpression.Type.GetGenericArguments()[1]);
170+
expression = Expression.Call(null, selectMethod, expression, lambdaExpression);
171+
172+
return expression;
173+
}
174+
}
175+
}
176+
#endif

src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IQueryable`.IncludeOptimized.cs" />
2121
<Compile Include="$(MSBuildThisFileDirectory)Extensions\IQueryable`.IncludeOptimizedByPath.cs" />
2222
<Compile Include="$(MSBuildThisFileDirectory)QueryIncludeOptimizedByPath.cs" />
23+
<Compile Include="$(MSBuildThisFileDirectory)QueryIncludeOptimizedByPath_EFCore.cs" />
2324
<Compile Include="$(MSBuildThisFileDirectory)QueryIncludeOptimizedChild`2.cs" />
2425
<Compile Include="$(MSBuildThisFileDirectory)QueryIncludeOptimizedExpressionReduceVisitor.cs" />
2526
<Compile Include="$(MSBuildThisFileDirectory)QueryIncludeOptimizedExpressionToReduceVisitor.cs" />

0 commit comments

Comments
 (0)