Skip to content

Commit 8fbec9b

Browse files
authored
Generating Parameterized SQL (by sspekinc) (#213)
* Generating Paratemeterized SQL Converting constant expressions to the wrapped ones will enable the generated SQL is to be parameterized as proposed in the following blog post. https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd * Update ExpressionHelper.cs * Update src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs Co-Authored-By: sspekinc <[email protected]> * Update src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs Co-Authored-By: sspekinc <[email protected]> * Update src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs Co-Authored-By: sspekinc <[email protected]> * Added credits link * Update DynamicExpressionParserTests.cs * Update appveyor.yml * Update DynamicExpressionParserTests.cs * Update Constants.cs * Update ExpressionHelper.cs * Update DynamicExpressionParserTests.cs * Update DynamicExpressionParserTests.cs * Update ExpressionHelper.cs * Update ExpressionHelper.cs * Update DynamicExpressionParserTests.cs * Update DynamicExpressionParserTests.cs * Refactor and move some classes * UseParameterizedNamesInDynamicQuery
1 parent a183117 commit 8fbec9b

11 files changed

+343
-86
lines changed

System.Linq.Dynamic.Core.sln

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ EndProject
2828
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.EntityFrameworkCore.DynamicLinq", "src\Microsoft.EntityFrameworkCore.DynamicLinq\Microsoft.EntityFrameworkCore.DynamicLinq.csproj", "{D3804228-91F4-4502-9595-39584E510001}"
2929
EndProject
3030
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Dynamic.Core.Tests", "test\System.Linq.Dynamic.Core.Tests\System.Linq.Dynamic.Core.Tests.csproj", "{912FBF24-3CAE-4A50-B5EA-E525B9FAEC80}"
31+
ProjectSection(ProjectDependencies) = postProject
32+
{D3804228-91F4-4502-9595-39584E510001} = {D3804228-91F4-4502-9595-39584E510001}
33+
EndProjectSection
3134
EndProject
3235
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFramework.DynamicLinq.Tests", "test\EntityFramework.DynamicLinq.Tests\EntityFramework.DynamicLinq.Tests.csproj", "{BF97CB1B-5043-4256-8F42-CF3A4F3863BE}"
3336
EndProject

src-console/ConsoleApp_net452_EF6/Program.cs

+13-9
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ class Program
1111
{
1212
static void Main(string[] args)
1313
{
14-
var all = new
15-
{
16-
test1 = new List<int> { 1, 2, 3 }.ToDynamicList(typeof(int)),
17-
test2 = new List<dynamic> { 4, 5, 6 }.ToDynamicList(typeof(int)),
18-
test3 = new List<object> { 7, 8, 9 }.ToDynamicList(typeof(int))
19-
};
20-
21-
Console.WriteLine("all {0}", JsonConvert.SerializeObject(all, Formatting.Indented));
2214
using (var context = new KendoGridDbContext())
2315
{
16+
var found1 = context.Employees.FirstOrDefault($"EmployeeNumber > 1000");
17+
Console.WriteLine($"found1 : {found1.Id} - {found1.EmployeeNumber}");
18+
19+
var found2 = context.Employees.FirstOrDefault($"EmployeeNumber > @0", 1001);
20+
Console.WriteLine($"found2 : {found2.Id} - {found2.EmployeeNumber}");
21+
22+
int em = 1002;
23+
var found3 = context.Employees.FirstOrDefault($"EmployeeNumber > @0", em);
24+
Console.WriteLine($"found3 : {found3.Id} - {found3.EmployeeNumber}");
25+
26+
return;
27+
2428
string search = "2";
2529
var expected = context.Employees.Where(e => System.Data.Entity.SqlServer.SqlFunctions.StringConvert((double)e.EmployeeNumber).Contains(search)).ToArray();
2630
foreach (var emp in expected)
@@ -34,7 +38,7 @@ static void Main(string[] args)
3438
Console.WriteLine($"DynamicLinq : {emp.Id} - {emp.EmployeeNumber}");
3539
}
3640

37-
Console.WriteLine(new String('-', 80));
41+
Console.WriteLine(new string('-', 80));
3842
int x = 1002;
3943
int seven = 7;
4044
var testNonOptimize = context.Employees.Where(e => e.EmployeeNumber > 1000 && e.EmployeeNumber < x && 7 == seven);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System.Linq.Expressions;
2+
using System.Reflection;
3+
4+
namespace System.Linq.Dynamic.Core.Parser
5+
{
6+
/// <summary>
7+
/// Based on gblog by graeme-hill. https://github.com/graeme-hill/gblog/blob/master/source_content/articles/2014.139_entity-framework-dynamic-queries-and-parameterization.mkd
8+
/// </summary>
9+
internal class ConstantExpressionWrapper : IConstantExpressionWrapper
10+
{
11+
public void Wrap(ref Expression expression)
12+
{
13+
if (expression is ConstantExpression)
14+
{
15+
var constantExpression = expression as ConstantExpression;
16+
17+
if (constantExpression.Type == typeof(bool))
18+
{
19+
expression = WrappedConstant((bool)constantExpression.Value);
20+
}
21+
else if (constantExpression.Type == typeof(string))
22+
{
23+
expression = WrappedConstant((string)constantExpression.Value);
24+
}
25+
else if (constantExpression.Type == typeof(float))
26+
{
27+
expression = WrappedConstant((float)constantExpression.Value);
28+
}
29+
else if (constantExpression.Type == typeof(decimal))
30+
{
31+
expression = WrappedConstant((decimal)constantExpression.Value);
32+
}
33+
else if (constantExpression.Type == typeof(double))
34+
{
35+
expression = WrappedConstant((double)constantExpression.Value);
36+
}
37+
else if (constantExpression.Type == typeof(long))
38+
{
39+
expression = WrappedConstant((long)constantExpression.Value);
40+
}
41+
else if (constantExpression.Type == typeof(ulong))
42+
{
43+
expression = WrappedConstant((ulong)constantExpression.Value);
44+
}
45+
else if (constantExpression.Type == typeof(int))
46+
{
47+
expression = WrappedConstant((int)constantExpression.Value);
48+
}
49+
else if (constantExpression.Type == typeof(uint))
50+
{
51+
expression = WrappedConstant((uint)constantExpression.Value);
52+
}
53+
else if (constantExpression.Type == typeof(short))
54+
{
55+
expression = WrappedConstant((short)constantExpression.Value);
56+
}
57+
else if (constantExpression.Type == typeof(ushort))
58+
{
59+
expression = WrappedConstant((ushort)constantExpression.Value);
60+
}
61+
else if (constantExpression.Type == typeof(Guid))
62+
{
63+
expression = WrappedConstant((Guid)constantExpression.Value);
64+
}
65+
else if (constantExpression.Type == typeof(DateTime))
66+
{
67+
expression = WrappedConstant((DateTime)constantExpression.Value);
68+
}
69+
else if (constantExpression.Type == typeof(DateTimeOffset))
70+
{
71+
expression = WrappedConstant((DateTimeOffset)constantExpression.Value);
72+
}
73+
else if (constantExpression.Type == typeof(TimeSpan))
74+
{
75+
expression = WrappedConstant((TimeSpan)constantExpression.Value);
76+
}
77+
78+
return;
79+
}
80+
81+
if (expression is NewExpression)
82+
{
83+
var newExpression = expression as NewExpression;
84+
85+
if (newExpression.Type == typeof(Guid))
86+
{
87+
expression = WrappedConstant(Expression.Lambda<Func<Guid>>(newExpression).Compile()());
88+
}
89+
else if (newExpression.Type == typeof(DateTime))
90+
{
91+
expression = WrappedConstant(Expression.Lambda<Func<DateTime>>(newExpression).Compile()());
92+
}
93+
else if (newExpression.Type == typeof(DateTimeOffset))
94+
{
95+
expression = WrappedConstant(Expression.Lambda<Func<DateTimeOffset>>(newExpression).Compile()());
96+
}
97+
else if (newExpression.Type == typeof(TimeSpan))
98+
{
99+
expression = WrappedConstant(Expression.Lambda<Func<TimeSpan>>(newExpression).Compile()());
100+
}
101+
}
102+
}
103+
104+
private static MemberExpression WrappedConstant<TValue>(TValue value)
105+
{
106+
var wrapper = new WrappedValue<TValue>(value);
107+
108+
return Expression.Property(Expression.Constant(wrapper), typeof(WrappedValue<TValue>).GetProperty("Value"));
109+
}
110+
}
111+
}

src/System.Linq.Dynamic.Core/Parser/ExpressionHelper.cs

+74-41
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,101 @@
1-
using System.Globalization;
1+
using JetBrains.Annotations;
2+
using System.Globalization;
3+
using System.Linq.Dynamic.Core.Validation;
24
using System.Linq.Expressions;
35
using System.Reflection;
46

57
namespace System.Linq.Dynamic.Core.Parser
68
{
7-
internal static class ExpressionHelper
9+
internal class ExpressionHelper : IExpressionHelper
810
{
9-
public static void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
11+
private readonly IConstantExpressionWrapper _constantExpressionWrapper = new ConstantExpressionWrapper();
12+
private readonly ParsingConfig _parsingConfig;
13+
14+
internal ExpressionHelper([NotNull] ParsingConfig parsingConfig)
15+
{
16+
Check.NotNull(parsingConfig, nameof(parsingConfig));
17+
18+
_parsingConfig = parsingConfig;
19+
}
20+
21+
public void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
1022
{
1123
if (left.Type == right.Type)
1224
{
1325
return;
1426
}
1527

16-
if (left.Type == typeof(UInt64) || right.Type == typeof(UInt64))
28+
if (left.Type == typeof(ulong) || right.Type == typeof(ulong))
1729
{
18-
right = right.Type != typeof(UInt64) ? Expression.Convert(right, typeof(UInt64)) : right;
19-
left = left.Type != typeof(UInt64) ? Expression.Convert(left, typeof(UInt64)) : left;
30+
right = right.Type != typeof(ulong) ? Expression.Convert(right, typeof(ulong)) : right;
31+
left = left.Type != typeof(ulong) ? Expression.Convert(left, typeof(ulong)) : left;
2032
}
21-
else if (left.Type == typeof(Int64) || right.Type == typeof(Int64))
33+
else if (left.Type == typeof(long) || right.Type == typeof(long))
2234
{
23-
right = right.Type != typeof(Int64) ? Expression.Convert(right, typeof(Int64)) : right;
24-
left = left.Type != typeof(Int64) ? Expression.Convert(left, typeof(Int64)) : left;
35+
right = right.Type != typeof(long) ? Expression.Convert(right, typeof(long)) : right;
36+
left = left.Type != typeof(long) ? Expression.Convert(left, typeof(long)) : left;
2537
}
26-
else if (left.Type == typeof(UInt32) || right.Type == typeof(UInt32))
38+
else if (left.Type == typeof(uint) || right.Type == typeof(uint))
2739
{
28-
right = right.Type != typeof(UInt32) ? Expression.Convert(right, typeof(UInt32)) : right;
29-
left = left.Type != typeof(UInt32) ? Expression.Convert(left, typeof(UInt32)) : left;
40+
right = right.Type != typeof(uint) ? Expression.Convert(right, typeof(uint)) : right;
41+
left = left.Type != typeof(uint) ? Expression.Convert(left, typeof(uint)) : left;
3042
}
31-
else if (left.Type == typeof(Int32) || right.Type == typeof(Int32))
43+
else if (left.Type == typeof(int) || right.Type == typeof(int))
3244
{
33-
right = right.Type != typeof(Int32) ? Expression.Convert(right, typeof(Int32)) : right;
34-
left = left.Type != typeof(Int32) ? Expression.Convert(left, typeof(Int32)) : left;
45+
right = right.Type != typeof(int) ? Expression.Convert(right, typeof(int)) : right;
46+
left = left.Type != typeof(int) ? Expression.Convert(left, typeof(int)) : left;
3547
}
36-
else if (left.Type == typeof(UInt16) || right.Type == typeof(UInt16))
48+
else if (left.Type == typeof(ushort) || right.Type == typeof(ushort))
3749
{
38-
right = right.Type != typeof(UInt16) ? Expression.Convert(right, typeof(UInt16)) : right;
39-
left = left.Type != typeof(UInt16) ? Expression.Convert(left, typeof(UInt16)) : left;
50+
right = right.Type != typeof(ushort) ? Expression.Convert(right, typeof(ushort)) : right;
51+
left = left.Type != typeof(ushort) ? Expression.Convert(left, typeof(ushort)) : left;
4052
}
41-
else if (left.Type == typeof(Int16) || right.Type == typeof(Int16))
53+
else if (left.Type == typeof(short) || right.Type == typeof(short))
4254
{
43-
right = right.Type != typeof(Int16) ? Expression.Convert(right, typeof(Int16)) : right;
44-
left = left.Type != typeof(Int16) ? Expression.Convert(left, typeof(Int16)) : left;
55+
right = right.Type != typeof(short) ? Expression.Convert(right, typeof(short)) : right;
56+
left = left.Type != typeof(short) ? Expression.Convert(left, typeof(short)) : left;
4557
}
46-
else if (left.Type == typeof(Byte) || right.Type == typeof(Byte))
58+
else if (left.Type == typeof(byte) || right.Type == typeof(byte))
4759
{
48-
right = right.Type != typeof(Byte) ? Expression.Convert(right, typeof(Byte)) : right;
49-
left = left.Type != typeof(Byte) ? Expression.Convert(left, typeof(Byte)) : left;
60+
right = right.Type != typeof(byte) ? Expression.Convert(right, typeof(byte)) : right;
61+
left = left.Type != typeof(byte) ? Expression.Convert(left, typeof(byte)) : left;
5062
}
5163
}
5264

53-
public static Expression GenerateAdd(Expression left, Expression right)
65+
public Expression GenerateAdd(Expression left, Expression right)
5466
{
5567
return Expression.Add(left, right);
5668
}
5769

58-
public static Expression GenerateStringConcat(Expression left, Expression right)
70+
public Expression GenerateStringConcat(Expression left, Expression right)
5971
{
6072
return GenerateStaticMethodCall("Concat", left, right);
6173
}
6274

63-
public static Expression GenerateSubtract(Expression left, Expression right)
75+
public Expression GenerateSubtract(Expression left, Expression right)
6476
{
6577
return Expression.Subtract(left, right);
6678
}
6779

68-
public static Expression GenerateEqual(Expression left, Expression right)
80+
public Expression GenerateEqual(Expression left, Expression right)
6981
{
7082
OptimizeForEqualityIfPossible(ref left, ref right);
83+
84+
WrapConstantExpressions(ref left, ref right);
85+
7186
return Expression.Equal(left, right);
7287
}
7388

74-
public static Expression GenerateNotEqual(Expression left, Expression right)
89+
public Expression GenerateNotEqual(Expression left, Expression right)
7590
{
7691
OptimizeForEqualityIfPossible(ref left, ref right);
92+
93+
WrapConstantExpressions(ref left, ref right);
94+
7795
return Expression.NotEqual(left, right);
7896
}
7997

80-
public static Expression GenerateGreaterThan(Expression left, Expression right)
98+
public Expression GenerateGreaterThan(Expression left, Expression right)
8199
{
82100
if (left.Type == typeof(string))
83101
{
@@ -91,10 +109,12 @@ public static Expression GenerateGreaterThan(Expression left, Expression right)
91109
return Expression.GreaterThan(leftPart, rightPart);
92110
}
93111

112+
WrapConstantExpressions(ref left, ref right);
113+
94114
return Expression.GreaterThan(left, right);
95115
}
96116

97-
public static Expression GenerateGreaterThanEqual(Expression left, Expression right)
117+
public Expression GenerateGreaterThanEqual(Expression left, Expression right)
98118
{
99119
if (left.Type == typeof(string))
100120
{
@@ -107,10 +127,12 @@ public static Expression GenerateGreaterThanEqual(Expression left, Expression ri
107127
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
108128
}
109129

130+
WrapConstantExpressions(ref left, ref right);
131+
110132
return Expression.GreaterThanOrEqual(left, right);
111133
}
112134

113-
public static Expression GenerateLessThan(Expression left, Expression right)
135+
public Expression GenerateLessThan(Expression left, Expression right)
114136
{
115137
if (left.Type == typeof(string))
116138
{
@@ -123,10 +145,12 @@ public static Expression GenerateLessThan(Expression left, Expression right)
123145
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
124146
}
125147

148+
WrapConstantExpressions(ref left, ref right);
149+
126150
return Expression.LessThan(left, right);
127151
}
128152

129-
public static Expression GenerateLessThanEqual(Expression left, Expression right)
153+
public Expression GenerateLessThanEqual(Expression left, Expression right)
130154
{
131155
if (left.Type == typeof(string))
132156
{
@@ -139,15 +163,15 @@ public static Expression GenerateLessThanEqual(Expression left, Expression right
139163
right.Type.GetTypeInfo().IsEnum ? Expression.Convert(right, Enum.GetUnderlyingType(right.Type)) : right);
140164
}
141165

166+
WrapConstantExpressions(ref left, ref right);
167+
142168
return Expression.LessThanOrEqual(left, right);
143169
}
144170

145-
public static void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right)
171+
public void OptimizeForEqualityIfPossible(ref Expression left, ref Expression right)
146172
{
147173
// The goal here is to provide the way to convert some types from the string form in a way that is compatible with Linq to Entities.
148-
//
149174
// The Expression.Call(typeof(Guid).GetMethod("Parse"), right); does the job only for Linq to Object but Linq to Entities.
150-
//
151175
Type leftType = left.Type;
152176
Type rightType = right.Type;
153177

@@ -162,7 +186,7 @@ public static void OptimizeForEqualityIfPossible(ref Expression left, ref Expres
162186
}
163187
}
164188

165-
public static Expression OptimizeStringForEqualityIfPossible(string text, Type type)
189+
public Expression OptimizeStringForEqualityIfPossible(string text, Type type)
166190
{
167191
if (type == typeof(DateTime) && DateTime.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
168192
{
@@ -186,20 +210,29 @@ public static Expression OptimizeStringForEqualityIfPossible(string text, Type t
186210
return null;
187211
}
188212

189-
static MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
213+
private MethodInfo GetStaticMethod(string methodName, Expression left, Expression right)
190214
{
191-
var methodInfo = left.Type.GetMethod(methodName, new[] {left.Type, right.Type});
215+
var methodInfo = left.Type.GetMethod(methodName, new[] { left.Type, right.Type });
192216
if (methodInfo == null)
193217
{
194-
methodInfo = right.Type.GetMethod(methodName, new[] {left.Type, right.Type});
218+
methodInfo = right.Type.GetMethod(methodName, new[] { left.Type, right.Type });
195219
}
196220

197221
return methodInfo;
198222
}
199223

200-
static Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
224+
private Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right)
201225
{
202226
return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });
203227
}
228+
229+
private void WrapConstantExpressions(ref Expression left, ref Expression right)
230+
{
231+
if (_parsingConfig.UseParameterizedNamesInDynamicQuery)
232+
{
233+
_constantExpressionWrapper.Wrap(ref left);
234+
_constantExpressionWrapper.Wrap(ref right);
235+
}
236+
}
204237
}
205238
}

0 commit comments

Comments
 (0)