Datei: NDODLL/Linq/ExpressionTreeTransformer.cs
Last Commit (f96ae8c)
1 | // |
2 | // Copyright (c) 2002-2016 Mirko Matytschak |
3 | // (www.netdataobjects.de) |
4 | // |
5 | // Author: Mirko Matytschak |
6 | // |
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
11 | // conditions: |
12 | |
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
14 | // of the Software. |
15 | // |
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. |
21 | |
22 | |
23 | using System; |
24 | using System.Collections.Generic; |
25 | using System.Collections; |
26 | using System.Text; |
27 | using System.Linq.Expressions; |
28 | using System.Linq; |
29 | |
30 | namespace NDO.Linq |
31 | { |
32 | ····/// <summary> |
33 | ····/// This class transforms Linq queries to NDOql |
34 | ····/// </summary> |
35 | ····public class ExpressionTreeTransformer |
36 | ····{ |
37 | ········class OperatorEntry |
38 | ········{ |
39 | ············public OperatorEntry( ExpressionType exprType, string replacement, int prec ) |
40 | ············{ |
41 | ················this.ExpressionType = exprType; |
42 | ················this.Precedence = prec; |
43 | ················this.Replacement = replacement; |
44 | ············} |
45 | ············public ExpressionType ExpressionType; |
46 | ············public string Replacement; |
47 | ············public int Precedence; |
48 | |
49 | ············public static bool IsComparison( ExpressionType expType ) |
50 | ············{ |
51 | ················return expType == ExpressionType.GreaterThanOrEqual || expType == ExpressionType.GreaterThan || expType == ExpressionType.LessThan || expType == ExpressionType.LessThanOrEqual || expType == ExpressionType.Equal || expType == ExpressionType.NotEqual; |
52 | ············} |
53 | ········} |
54 | |
55 | ········static OperatorEntry[] operators = |
56 | ········{ |
57 | ············new OperatorEntry(ExpressionType.Modulo, "%", 0), |
58 | ············new OperatorEntry(ExpressionType.Multiply, "*", 0), |
59 | ············new OperatorEntry(ExpressionType.Divide, "/", 0), |
60 | ············new OperatorEntry(ExpressionType.Add, "+", 1), |
61 | ············new OperatorEntry(ExpressionType.Subtract, "-", 1), |
62 | ············new OperatorEntry(ExpressionType.GreaterThanOrEqual, ">=", 2), |
63 | ············new OperatorEntry(ExpressionType.GreaterThan, ">", 2), |
64 | ············new OperatorEntry(ExpressionType.LessThanOrEqual, "<=", 2), |
65 | ············new OperatorEntry(ExpressionType.LessThan, "<", 2), |
66 | ············new OperatorEntry(ExpressionType.NotEqual, "<>", 2), |
67 | ············new OperatorEntry(ExpressionType.Equal, "=", 3), |
68 | ············new OperatorEntry(ExpressionType.And, "AND", 4), |
69 | ············new OperatorEntry(ExpressionType.AndAlso, "AND", 4), |
70 | ············new OperatorEntry(ExpressionType.Or, "OR", 5), |
71 | ············new OperatorEntry(ExpressionType.OrElse, "OR", 5), |
72 | ········}; |
73 | |
74 | ········LambdaExpression baseExpression; |
75 | ········Expression baseLeftSide; |
76 | ········StringBuilder sb; |
77 | ········List<object> parameters; |
78 | ········string baseParameterName; |
79 | ········int baseParameterLength; |
80 | ········Stack<Expression> expressionStack; |
81 | |
82 | ········/// <summary> |
83 | ········/// Returns the generated parameters |
84 | ········/// </summary> |
85 | ········public IEnumerable<object> Parameters |
86 | ········{ |
87 | ············get { return parameters; } |
88 | ········} |
89 | |
90 | ········/// <summary> |
91 | ········/// Constructs an ExpressionTreeTransformer objct |
92 | ········/// </summary> |
93 | ········/// <param name="ex"></param> |
94 | ········public ExpressionTreeTransformer( LambdaExpression ex ) |
95 | ········{ |
96 | ············this.baseExpression = ex; |
97 | ········} |
98 | |
99 | ········/// <summary> |
100 | ········/// Transforms the lambda expression passed to the constructor into an NDOql string. |
101 | ········/// </summary> |
102 | ········/// <returns></returns> |
103 | ········public string Transform() |
104 | ········{ |
105 | ············baseParameterName = baseExpression.Parameters[0].Name; |
106 | ············baseLeftSide = baseExpression.Parameters[0]; |
107 | ············baseParameterLength = baseParameterName.Length + 1; |
108 | ············this.expressionStack = new Stack<Expression>(); |
109 | ············sb = new StringBuilder(); |
110 | ············parameters = new List<object>(); |
111 | ············Transform( baseExpression.Body ); |
112 | ············return sb.ToString(); |
113 | ········} |
114 | |
115 | ········bool IsNull( Expression ex ) |
116 | ········{ |
117 | ············if (ex is ConstantExpression ce) |
118 | ············{ |
119 | ················if (ce.Value == null) |
120 | ····················return true; |
121 | ············} |
122 | |
123 | ············if (ex.NodeType == ExpressionType.MemberAccess) |
124 | ············{ |
125 | ················var me = ex as MemberExpression; |
126 | ················if (me != null) |
127 | ················{ |
128 | ····················if (me.Type == typeof( DateTime )) |
129 | ····················{ |
130 | ························try |
131 | ························{ |
132 | ····························var d = Expression.Lambda( ex ).Compile(); |
133 | ····························var dt = (DateTime)d.DynamicInvoke(); |
134 | ····························if (dt == DateTime.MinValue) |
135 | ································return true; |
136 | ························} |
137 | ························catch { } |
138 | ····················} |
139 | ····················else if (me.Type == typeof( Guid )) |
140 | ····················{ |
141 | ························try |
142 | ························{ |
143 | ····························var d = Expression.Lambda( ex ).Compile(); |
144 | ····························var g = (Guid)d.DynamicInvoke(); |
145 | ····························if (g == Guid.Empty) |
146 | ································return true; |
147 | ························} |
148 | ························catch { } |
149 | ····················} |
150 | ················} |
151 | ············} |
152 | |
153 | ············return false; |
154 | ········} |
155 | |
156 | ········void TransformBinaryOperator( ExpressionType exprType, ref Expression right ) |
157 | ········{ |
158 | ············sb.Append( ' ' ); |
159 | ············if (IsNull( right )) |
160 | ············{ |
161 | ················if (exprType == ExpressionType.NotEqual) |
162 | ················{ |
163 | ····················sb.Append( "IS NOT" ); |
164 | ····················right = Expression.Constant(null); |
165 | ················} |
166 | ················else if (exprType == ExpressionType.Equal) |
167 | ················{ |
168 | ····················sb.Append( "IS" ); |
169 | ····················right = Expression.Constant( null ); |
170 | ················} |
171 | ················else |
172 | ················{ |
173 | ····················OperatorEntry oe = FindOperator( exprType ); |
174 | ····················sb.Append( oe.Replacement ); |
175 | ················} |
176 | ············} |
177 | ············else |
178 | ············{ |
179 | ················OperatorEntry oe = FindOperator( exprType ); |
180 | ················sb.Append( oe.Replacement ); |
181 | ············} |
182 | ············sb.Append( ' ' ); |
183 | ········} |
184 | |
185 | ········OperatorEntry FindOperator( ExpressionType exprType ) |
186 | ········{ |
187 | ············foreach (OperatorEntry oe in operators) |
188 | ················if (oe.ExpressionType == exprType) |
189 | ····················return oe; |
190 | ············throw new Exception( "ExpressionTreeTransformer: Unsupported operator: " + exprType ); |
191 | ········} |
192 | |
193 | ········int GetPrecedence( ExpressionType exprType ) |
194 | ········{ |
195 | ············OperatorEntry oe = FindOperator(exprType); |
196 | ············return oe.Precedence; |
197 | ········} |
198 | |
199 | ········void AddParameter( object value ) |
200 | ········{ |
201 | ············var ix = parameters.FindIndex(p => p.Equals(value)); |
202 | ············if (ix > -1) |
203 | ············{ |
204 | ················sb.Append( '{' ); |
205 | ················sb.Append( ix.ToString() ); |
206 | ················sb.Append( '}' ); |
207 | ············} |
208 | ············else |
209 | ············{ |
210 | ················sb.Append( '{' ); |
211 | ················sb.Append( parameters.Count.ToString() ); |
212 | ················sb.Append( '}' ); |
213 | ················parameters.Add( value ); |
214 | ············} |
215 | ········} |
216 | |
217 | ········BinaryExpression FlipExpression( BinaryExpression binex ) |
218 | ········{ |
219 | ············if (binex.NodeType == ExpressionType.GreaterThan) |
220 | ················return BinaryExpression.LessThan( binex.Right, binex.Left ); |
221 | ············if (binex.NodeType == ExpressionType.GreaterThanOrEqual) |
222 | ················return BinaryExpression.LessThanOrEqual( binex.Right, binex.Left ); |
223 | ············if (binex.NodeType == ExpressionType.LessThan) |
224 | ················return BinaryExpression.GreaterThan( binex.Right, binex.Left ); |
225 | ············if (binex.NodeType == ExpressionType.LessThanOrEqual) |
226 | ················return BinaryExpression.GreaterThanOrEqual( binex.Right, binex.Left ); |
227 | ············return Expression.MakeBinary( binex.NodeType, binex.Right, binex.Left ); |
228 | ········} |
229 | |
230 | ········void FlipArguments( List<Expression> arguments ) |
231 | ········{ |
232 | ············var temp = arguments[0]; |
233 | ············arguments[0] = arguments[1]; |
234 | ············arguments[1] = temp; |
235 | ········} |
236 | |
237 | ········bool IsReversedExpression( List<Expression> arguments ) |
238 | ········{ |
239 | ············return arguments.Count == 2 && arguments[0] is ConstantExpression && !( arguments[1] is ConstantExpression ); |
240 | ········} |
241 | |
242 | ········void TransformEquality(Expression ex1, Expression ex2) |
243 | ········{ |
244 | ············Transform( ex1 ); |
245 | ············TransformBinaryOperator( ExpressionType.Equal, ref ex2 ); |
246 | ············Transform( ex2 ); |
247 | ········} |
248 | |
249 | ········bool IsPropertyOfBaseExpression( Expression ex ) |
250 | ········{ |
251 | ············if (ex is MemberExpression pe) |
252 | ············{ |
253 | ················return pe.Expression == baseLeftSide; |
254 | ············} |
255 | |
256 | ············return false; |
257 | ········} |
258 | |
259 | ········void Transform( Expression ex ) |
260 | ········{ |
261 | ············expressionStack.Push( ex );··// This helps determining parent expressions |
262 | ············try |
263 | ············{ |
264 | ················if (ex == this.baseLeftSide) |
265 | ····················return; |
266 | ··················//------- |
267 | |
268 | ················string exStr = ex.ToString(); |
269 | ················if (exStr == "null") |
270 | ················{ |
271 | ····················sb.Append( "NULL" ); |
272 | ····················return; |
273 | ··················//------- |
274 | ················} |
275 | |
276 | ················if (ex.NodeType == ExpressionType.MemberAccess ) |
277 | ················{ |
278 | ····················// We try to compile the expression. |
279 | ····················// If it can be compiled, we have a value, |
280 | ····················// which should be added as a parameter. |
281 | ····················// We are safe to examine MemberAccess nodes only, |
282 | ····················// because local variables will be wrapped in |
283 | ····················// display classes and accessed as members of |
284 | ····················// these classes. All other cases are anyway |
285 | ····················// members of arbitrary types (like String.Empty). |
286 | ····················try |
287 | ····················{ |
288 | ························var d = Expression.Lambda( ex ).Compile(); |
289 | ························AddParameter( d.DynamicInvoke() ); |
290 | ························return; |
291 | ······················//------- |
292 | ····················} |
293 | ····················catch |
294 | ····················{··// Can't be compiled, so pass on. |
295 | ····················} |
296 | ················} |
297 | |
298 | ················BinaryExpression binex = ex as BinaryExpression; |
299 | ················if (binex != null) |
300 | ················{ |
301 | ····················Expression left = binex.Left; |
302 | ····················Expression right = binex.Right; |
303 | ····················if (left.NodeType == ExpressionType.Convert) |
304 | ························left = ( (UnaryExpression) left ).Operand; |
305 | ····················if (right.NodeType == ExpressionType.Convert) |
306 | ························right = ( (UnaryExpression) right ).Operand; |
307 | |
308 | ····················if (!IsPropertyOfBaseExpression( binex.Left ) && IsPropertyOfBaseExpression( binex.Right )) |
309 | ····················{ |
310 | ························Transform( FlipExpression( binex ) ); |
311 | ························return; |
312 | ························//------- |
313 | ····················} |
314 | |
315 | ····················int ownPrecedence = GetPrecedence(binex.NodeType); |
316 | ····················int childPrecedence = 0; |
317 | ····················BinaryExpression childBinex = binex.Left as BinaryExpression; |
318 | ····················bool leftbracket = false; |
319 | ····················if (childBinex != null) |
320 | ····················{ |
321 | ························childPrecedence = Math.Max( childPrecedence, GetPrecedence( childBinex.NodeType ) ); |
322 | ························leftbracket = childPrecedence > ownPrecedence; |
323 | ····················} |
324 | ····················childBinex = binex.Right as BinaryExpression; |
325 | ····················bool rightbracket = false; |
326 | ····················if (childBinex != null) |
327 | ····················{ |
328 | ························childPrecedence = Math.Max( childPrecedence, GetPrecedence( childBinex.NodeType ) ); |
329 | ························rightbracket = childPrecedence > ownPrecedence; |
330 | ····················} |
331 | ····················if (leftbracket) |
332 | ························sb.Append( '(' ); |
333 | ····················Transform( left ); |
334 | ····················if (leftbracket) |
335 | ························sb.Append( ')' ); |
336 | ····················TransformBinaryOperator( ex.NodeType, ref right ); |
337 | ····················if (rightbracket) |
338 | ························sb.Append( '(' ); |
339 | ····················Transform( right ); |
340 | ····················if (rightbracket) |
341 | ························sb.Append( ')' ); |
342 | ····················return; |
343 | ····················//------- |
344 | ················} |
345 | |
346 | ················MethodCallExpression mcex = ex as MethodCallExpression; |
347 | ················if (mcex != null) |
348 | ················{ |
349 | ····················var arguments = new List<Expression>(mcex.Arguments); |
350 | ····················string mname = mcex.Method.Name; |
351 | ····················if (mname == "op_Equality") |
352 | ····················{ |
353 | ························if (IsReversedExpression( arguments )) |
354 | ························{ |
355 | ····························FlipArguments( arguments ); |
356 | ························} |
357 | ························TransformEquality( arguments[0], arguments[1] ); |
358 | ····················} |
359 | ····················else if (mname == "Equals") |
360 | ····················{ |
361 | ························TransformEquality( mcex.Object, arguments[0] ); |
362 | ····················} |
363 | ····················else if (mname == "Like") |
364 | ····················{ |
365 | ························if (IsReversedExpression( arguments )) |
366 | ························{ |
367 | ····························FlipArguments( arguments ); |
368 | ························} |
369 | ························Transform( arguments[0] ); |
370 | ························sb.Append( " LIKE " ); |
371 | ························Transform( arguments[1] ); |
372 | ····················} |
373 | ····················else if (mname == "GreaterEqual") |
374 | ····················{ |
375 | ························string op = " >= "; |
376 | ························if (IsReversedExpression( arguments )) |
377 | ························{ |
378 | ····························FlipArguments( arguments ); |
379 | ····························op = " <= "; |
380 | ························} |
381 | ························Transform( arguments[0] ); |
382 | ························sb.Append( op ); |
383 | ························Transform( arguments[1] ); |
384 | ····················} |
385 | ····················else if (mname == "LowerEqual") |
386 | ····················{ |
387 | ························string op = " <= "; |
388 | ························if (IsReversedExpression( arguments )) |
389 | ························{ |
390 | ····························FlipArguments( arguments ); |
391 | ····························op = " >= "; |
392 | ························} |
393 | ························Transform( arguments[0] ); |
394 | ························sb.Append( op ); |
395 | ························Transform( arguments[1] ); |
396 | ····················} |
397 | ····················else if (mname == "GreaterThan") |
398 | ····················{ |
399 | ························string op = " > "; |
400 | ························if (IsReversedExpression( arguments )) |
401 | ························{ |
402 | ····························FlipArguments( arguments ); |
403 | ····························op = " < "; |
404 | ························} |
405 | ························Transform( arguments[0] ); |
406 | ························sb.Append( op ); |
407 | ························Transform( arguments[1] ); |
408 | ····················} |
409 | ····················else if (mname == "LowerThan") |
410 | ····················{ |
411 | ························string op = " < "; |
412 | ························if (IsReversedExpression( arguments )) |
413 | ························{ |
414 | ····························FlipArguments( arguments ); |
415 | ····························op = " > "; |
416 | ························} |
417 | ························Transform( arguments[0] ); |
418 | ························sb.Append( op ); |
419 | ························Transform( arguments[1] ); |
420 | ····················} |
421 | ····················else if (mname == "Between") |
422 | ····················{ |
423 | ························Transform( mcex.Arguments[0] ); |
424 | ························sb.Append( " BETWEEN " ); |
425 | ························Transform( mcex.Arguments[1] ); |
426 | ························sb.Append( " AND " ); |
427 | ························Transform( mcex.Arguments[2] ); |
428 | ····················} |
429 | ····················else if (mname == "get_Item") |
430 | ····················{ |
431 | ························string argStr = mcex.Arguments[0].ToString(); |
432 | ························if (argStr == "Any.Index") |
433 | ························{ |
434 | ····························Transform( mcex.Object ); |
435 | ····························return; |
436 | ····························//------- |
437 | ························} |
438 | ························Transform( mcex.Object ); |
439 | ························if (mcex.Object.Type.FullName == "NDO.ObjectId") |
440 | ························{ |
441 | ····························TransformOidIndex( mcex.Arguments[0] ); |
442 | ····························return; |
443 | ····························//------- |
444 | ························} |
445 | ························sb.Append( '(' ); |
446 | ························Transform( mcex.Arguments[0] ); |
447 | ························sb.Append( ')' ); |
448 | ····················} |
449 | ····················else if (mname == "ElementAt") |
450 | ····················{ |
451 | ························string argStr = mcex.Arguments[1].ToString(); |
452 | ························if (argStr == "Any.Index" || "Index.Any" == argStr) |
453 | ························{ |
454 | ····························Transform( mcex.Arguments[0] ); |
455 | ····························return; |
456 | ····························//------- |
457 | ························} |
458 | ························Transform( mcex.Arguments[0] ); |
459 | ························sb.Append( '(' ); |
460 | ························Transform( mcex.Arguments[1] ); |
461 | ························sb.Append( ')' ); |
462 | ····················} |
463 | ····················else if (mname == "Any") |
464 | ····················{ |
465 | ························Transform( mcex.Arguments[0] ); |
466 | ························var exprx = mcex.Arguments[1]; |
467 | ························if (sb[sb.Length - 1] != '.') |
468 | ····························sb.Append( '.' ); |
469 | ························Transform( ( (LambdaExpression) mcex.Arguments[1] ).Body ); |
470 | ····················} |
471 | ····················else if (mname == "In") |
472 | ····················{ |
473 | ························var arg = mcex.Arguments[1]; |
474 | ························IEnumerable list = (IEnumerable)(Expression.Lambda(arg).Compile().DynamicInvoke()); |
475 | ························Transform( mcex.Arguments[0] ); |
476 | ························sb.Append( " IN (" ); |
477 | ························var en = list.GetEnumerator(); |
478 | ························en.MoveNext(); |
479 | ························bool doQuote = en.Current is string || en.Current is Guid || en.Current is DateTime; |
480 | foreach ( object obj in list) |
481 | ························{ |
482 | ····························if (doQuote) |
483 | ································sb.Append( '\'' ); |
484 | ····························sb.Append( obj ); |
485 | ····························if (doQuote) |
486 | ································sb.Append( '\'' ); |
487 | ····························sb.Append( ',' ); |
488 | ························} |
489 | ························sb.Length -= 1; |
490 | ························sb.Append( ')' ); |
491 | ····················} |
492 | ····················else if (mname == "Oid") |
493 | ····················{ |
494 | ························var sbLength = sb.Length; |
495 | ························Transform( mcex.Arguments[0] ); |
496 | ························if (sb.Length > sbLength) |
497 | ····························sb.Append( ".oid" ); |
498 | ························else |
499 | ····························sb.Append( "oid" ); |
500 | ························if (mcex.Arguments.Count > 1) |
501 | ························{ |
502 | ····························TransformOidIndex( mcex.Arguments[1] ); |
503 | ························} |
504 | ····················} |
505 | ····················else··// Assume, we have a server function here |
506 | ····················{ |
507 | ························var method = mcex.Method; |
508 | ························var attrs = method.GetCustomAttributes( typeof( ServerFunctionAttribute ), true ); |
509 | ························if (attrs.Length > 0) |
510 | ····························mname = ((ServerFunctionAttribute) attrs[0]).Name; |
511 | ························sb.Append( mname ); |
512 | ························sb.Append( '(' ); |
513 | ························var end = arguments.Count - 1; |
514 | ························var i = 0; |
515 | ························foreach (var arg in arguments) |
516 | ························{ |
517 | ····························Transform( arg ); |
518 | ····························if (i < end) |
519 | ································sb.Append( ", " ); |
520 | ····························i++; |
521 | ························} |
522 | ························sb.Append( ')' ); |
523 | ····················} |
524 | ····················return; |
525 | ····················//------- |
526 | ················} |
527 | |
528 | ················if (ex.NodeType == ExpressionType.MemberAccess) |
529 | ················{ |
530 | ····················MemberExpression memberex = (MemberExpression) ex; |
531 | ····················if (memberex.Member.Name == "NDOObjectId") |
532 | ····················{ |
533 | ························if (memberex.Expression.ToString() != this.baseParameterName) |
534 | ························{ |
535 | ····························Transform( memberex.Expression ); |
536 | ····························if (sb[sb.Length - 1] != '.') |
537 | ································sb.Append( '.' ); |
538 | ························} |
539 | ························sb.Append( "oid" ); |
540 | ························return; |
541 | ····················} |
542 | ····················if (ex.Type == typeof( bool )) |
543 | ····················{ |
544 | ························var top = expressionStack.Pop(); |
545 | ························bool transformIt = expressionStack.Count == 0;··// The boolean expression is the top |
546 | ························if (expressionStack.Count > 0) |
547 | ························{ |
548 | ····························var parent = expressionStack.Peek(); |
549 | ····························if (parent.NodeType != ExpressionType.Equal && parent.NodeType != ExpressionType.NotEqual) |
550 | ····························{ |
551 | ································// A Boolean Expression, which is not part of an Equals or NotEquals expression, |
552 | ································// must be unary. Since Sql Server doesn't support unary boolean expressions, we add an Equals Expression which compares with 1. |
553 | ································// Fortunately this works with other databases, too. |
554 | ································transformIt = true; |
555 | ····························} |
556 | ························} |
557 | ························if (transformIt) |
558 | ························{ |
559 | ····························sb.Append( memberex.Member.Name ); |
560 | ····························sb.Append( " = 1" ); |
561 | ························} |
562 | ························expressionStack.Push( top ); |
563 | ························if (transformIt) |
564 | ····························return; |
565 | ························//------- |
566 | ····················} |
567 | ····················if (memberex.Expression != baseLeftSide) |
568 | ····················{ |
569 | ························// This happens while navigating through relations. |
570 | ························Transform( memberex.Expression ); |
571 | ························if (sb[sb.Length - 1] != '.') |
572 | ····························sb.Append( '.' ); |
573 | ····················} |
574 | ····················sb.Append( memberex.Member.Name ); |
575 | ····················return; |
576 | ····················//------- |
577 | ················} |
578 | |
579 | ················else if (ex.NodeType == ExpressionType.Constant) |
580 | ················{ |
581 | ····················ConstantExpression constEx = (ConstantExpression) ex; |
582 | ····················AddParameter( constEx.Value ); |
583 | ····················return; |
584 | ····················//------- |
585 | ················} |
586 | |
587 | ················else if (ex.NodeType == ExpressionType.Convert) |
588 | ················{ |
589 | ····················Transform( ( (UnaryExpression) ex ).Operand ); |
590 | ····················return; |
591 | ····················//------- |
592 | ················} |
593 | |
594 | ················else if (ex.NodeType == ExpressionType.Not) |
595 | ················{ |
596 | ····················sb.Append( " NOT " ); |
597 | ····················Expression inner = ((UnaryExpression)ex).Operand; |
598 | ····················var binary = (inner is BinaryExpression); |
599 | ····················if (binary) |
600 | ························sb.Append( '(' ); |
601 | ····················Transform( inner ); |
602 | ····················if (binary) |
603 | ························sb.Append( ')' ); |
604 | ····················return; |
605 | ····················//------- |
606 | ················} |
607 | ············} |
608 | ············finally |
609 | ············{················ |
610 | ················expressionStack.Pop(); |
611 | ············} |
612 | ········} |
613 | |
614 | ········private void TransformEquality( List<Expression> arguments ) |
615 | ········{ |
616 | ············Transform( arguments[0] ); |
617 | ············sb.Append( " = " ); |
618 | ············Transform( arguments[1] ); |
619 | ········} |
620 | |
621 | ········private void TransformOidIndex( Expression ex ) |
622 | ········{ |
623 | ············var ce = ex as ConstantExpression; |
624 | ············if (ce == null) |
625 | ················throw new Exception( "Oid index expression must be a ConstantExpression" ); |
626 | ············if (!( ce.Value is System.Int32 )) |
627 | ················throw new Exception( "Oid index expression must be an Int32" ); |
628 | |
629 | ············if ((int) ce.Value != 0) |
630 | ············{ |
631 | ················sb.Append( '(' ); |
632 | ················sb.Append( ce.Value ); |
633 | ················sb.Append( ')' ); |
634 | ············} |
635 | ········} |
636 | ····} |
637 | } |
New Commit (fe801ed)
1 | // |
2 | // Copyright (c) 2002-2016 Mirko Matytschak |
3 | // (www.netdataobjects.de) |
4 | // |
5 | // Author: Mirko Matytschak |
6 | // |
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
8 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation |
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
10 | // Software, and to permit persons to whom the Software is furnished to do so, subject to the following |
11 | // conditions: |
12 | |
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions |
14 | // of the Software. |
15 | // |
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
17 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
19 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
20 | // DEALINGS IN THE SOFTWARE. |
21 | |
22 | |
23 | using System; |
24 | using System.Collections.Generic; |
25 | using System.Collections; |
26 | using System.Text; |
27 | using System.Linq.Expressions; |
28 | using System.Linq; |
29 | |
30 | namespace NDO.Linq |
31 | { |
32 | ····/// <summary> |
33 | ····/// This class transforms Linq queries to NDOql |
34 | ····/// </summary> |
35 | ····public class ExpressionTreeTransformer |
36 | ····{ |
37 | ········class OperatorEntry |
38 | ········{ |
39 | ············public OperatorEntry( ExpressionType exprType, string replacement, int prec ) |
40 | ············{ |
41 | ················this.ExpressionType = exprType; |
42 | ················this.Precedence = prec; |
43 | ················this.Replacement = replacement; |
44 | ············} |
45 | ············public ExpressionType ExpressionType; |
46 | ············public string Replacement; |
47 | ············public int Precedence; |
48 | |
49 | ············public static bool IsComparison( ExpressionType expType ) |
50 | ············{ |
51 | ················return expType == ExpressionType.GreaterThanOrEqual || expType == ExpressionType.GreaterThan || expType == ExpressionType.LessThan || expType == ExpressionType.LessThanOrEqual || expType == ExpressionType.Equal || expType == ExpressionType.NotEqual; |
52 | ············} |
53 | ········} |
54 | |
55 | ········static OperatorEntry[] operators = |
56 | ········{ |
57 | ············new OperatorEntry(ExpressionType.Modulo, "%", 0), |
58 | ············new OperatorEntry(ExpressionType.Multiply, "*", 0), |
59 | ············new OperatorEntry(ExpressionType.Divide, "/", 0), |
60 | ············new OperatorEntry(ExpressionType.Add, "+", 1), |
61 | ············new OperatorEntry(ExpressionType.Subtract, "-", 1), |
62 | ············new OperatorEntry(ExpressionType.GreaterThanOrEqual, ">=", 2), |
63 | ············new OperatorEntry(ExpressionType.GreaterThan, ">", 2), |
64 | ············new OperatorEntry(ExpressionType.LessThanOrEqual, "<=", 2), |
65 | ············new OperatorEntry(ExpressionType.LessThan, "<", 2), |
66 | ············new OperatorEntry(ExpressionType.NotEqual, "<>", 2), |
67 | ············new OperatorEntry(ExpressionType.Equal, "=", 3), |
68 | ············new OperatorEntry(ExpressionType.And, "AND", 4), |
69 | ············new OperatorEntry(ExpressionType.AndAlso, "AND", 4), |
70 | ············new OperatorEntry(ExpressionType.Or, "OR", 5), |
71 | ············new OperatorEntry(ExpressionType.OrElse, "OR", 5), |
72 | ········}; |
73 | |
74 | ········LambdaExpression baseExpression; |
75 | ········Expression baseLeftSide; |
76 | ········StringBuilder sb; |
77 | ········List<object> parameters; |
78 | ········string baseParameterName; |
79 | ········int baseParameterLength; |
80 | ········Stack<Expression> expressionStack; |
81 | |
82 | ········/// <summary> |
83 | ········/// Returns the generated parameters |
84 | ········/// </summary> |
85 | ········public IEnumerable<object> Parameters |
86 | ········{ |
87 | ············get { return parameters; } |
88 | ········} |
89 | |
90 | ········/// <summary> |
91 | ········/// Constructs an ExpressionTreeTransformer objct |
92 | ········/// </summary> |
93 | ········/// <param name="ex"></param> |
94 | ········public ExpressionTreeTransformer( LambdaExpression ex ) |
95 | ········{ |
96 | ············this.baseExpression = ex; |
97 | ········} |
98 | |
99 | ········/// <summary> |
100 | ········/// Transforms the lambda expression passed to the constructor into an NDOql string. |
101 | ········/// </summary> |
102 | ········/// <returns></returns> |
103 | ········public string Transform() |
104 | ········{ |
105 | ············baseParameterName = baseExpression.Parameters[0].Name; |
106 | ············baseLeftSide = baseExpression.Parameters[0]; |
107 | ············baseParameterLength = baseParameterName.Length + 1; |
108 | ············this.expressionStack = new Stack<Expression>(); |
109 | ············sb = new StringBuilder(); |
110 | ············parameters = new List<object>(); |
111 | ············Transform( baseExpression.Body ); |
112 | ············return sb.ToString(); |
113 | ········} |
114 | |
115 | ········bool IsNull( Expression ex ) |
116 | ········{ |
117 | ············if (ex is ConstantExpression ce) |
118 | ············{ |
119 | ················if (ce.Value == null) |
120 | ····················return true; |
121 | ············} |
122 | |
123 | ············if (ex.NodeType == ExpressionType.MemberAccess) |
124 | ············{ |
125 | ················var me = ex as MemberExpression; |
126 | ················if (me != null) |
127 | ················{ |
128 | ····················if (me.Type == typeof( DateTime )) |
129 | ····················{ |
130 | ························try |
131 | ························{ |
132 | ····························var d = Expression.Lambda( ex ).Compile(); |
133 | ····························var dt = (DateTime)d.DynamicInvoke(); |
134 | ····························if (dt == DateTime.MinValue) |
135 | ································return true; |
136 | ························} |
137 | ························catch { } |
138 | ····················} |
139 | ····················else if (me.Type == typeof( Guid )) |
140 | ····················{ |
141 | ························try |
142 | ························{ |
143 | ····························var d = Expression.Lambda( ex ).Compile(); |
144 | ····························var g = (Guid)d.DynamicInvoke(); |
145 | ····························if (g == Guid.Empty) |
146 | ································return true; |
147 | ························} |
148 | ························catch { } |
149 | ····················} |
150 | ················} |
151 | ············} |
152 | |
153 | ············return false; |
154 | ········} |
155 | |
156 | ········void TransformBinaryOperator( ExpressionType exprType, ref Expression right ) |
157 | ········{ |
158 | ············sb.Append( ' ' ); |
159 | ············if (IsNull( right )) |
160 | ············{ |
161 | ················if (exprType == ExpressionType.NotEqual) |
162 | ················{ |
163 | ····················sb.Append( "IS NOT" ); |
164 | ····················right = Expression.Constant(null); |
165 | ················} |
166 | ················else if (exprType == ExpressionType.Equal) |
167 | ················{ |
168 | ····················sb.Append( "IS" ); |
169 | ····················right = Expression.Constant( null ); |
170 | ················} |
171 | ················else |
172 | ················{ |
173 | ····················OperatorEntry oe = FindOperator( exprType ); |
174 | ····················sb.Append( oe.Replacement ); |
175 | ················} |
176 | ············} |
177 | ············else |
178 | ············{ |
179 | ················OperatorEntry oe = FindOperator( exprType ); |
180 | ················sb.Append( oe.Replacement ); |
181 | ············} |
182 | ············sb.Append( ' ' ); |
183 | ········} |
184 | |
185 | ········OperatorEntry FindOperator( ExpressionType exprType ) |
186 | ········{ |
187 | ············foreach (OperatorEntry oe in operators) |
188 | ················if (oe.ExpressionType == exprType) |
189 | ····················return oe; |
190 | ············throw new Exception( "ExpressionTreeTransformer: Unsupported operator: " + exprType ); |
191 | ········} |
192 | |
193 | ········int GetPrecedence( ExpressionType exprType ) |
194 | ········{ |
195 | ············OperatorEntry oe = FindOperator(exprType); |
196 | ············return oe.Precedence; |
197 | ········} |
198 | |
199 | ········void AddParameter( object value ) |
200 | ········{ |
201 | ············var ix = parameters.FindIndex(p => p.Equals(value)); |
202 | ············if (ix > -1) |
203 | ············{ |
204 | ················sb.Append( '{' ); |
205 | ················sb.Append( ix.ToString() ); |
206 | ················sb.Append( '}' ); |
207 | ············} |
208 | ············else |
209 | ············{ |
210 | ················sb.Append( '{' ); |
211 | ················sb.Append( parameters.Count.ToString() ); |
212 | ················sb.Append( '}' ); |
213 | ················parameters.Add( value ); |
214 | ············} |
215 | ········} |
216 | |
217 | ········BinaryExpression FlipExpression( BinaryExpression binex ) |
218 | ········{ |
219 | ············if (binex.NodeType == ExpressionType.GreaterThan) |
220 | ················return BinaryExpression.LessThan( binex.Right, binex.Left ); |
221 | ············if (binex.NodeType == ExpressionType.GreaterThanOrEqual) |
222 | ················return BinaryExpression.LessThanOrEqual( binex.Right, binex.Left ); |
223 | ············if (binex.NodeType == ExpressionType.LessThan) |
224 | ················return BinaryExpression.GreaterThan( binex.Right, binex.Left ); |
225 | ············if (binex.NodeType == ExpressionType.LessThanOrEqual) |
226 | ················return BinaryExpression.GreaterThanOrEqual( binex.Right, binex.Left ); |
227 | ············return Expression.MakeBinary( binex.NodeType, binex.Right, binex.Left ); |
228 | ········} |
229 | |
230 | ········void FlipArguments( List<Expression> arguments ) |
231 | ········{ |
232 | ············var temp = arguments[0]; |
233 | ············arguments[0] = arguments[1]; |
234 | ············arguments[1] = temp; |
235 | ········} |
236 | |
237 | ········bool IsReversedExpression( List<Expression> arguments ) |
238 | ········{ |
239 | ············return arguments.Count == 2 && arguments[0] is ConstantExpression && !( arguments[1] is ConstantExpression ); |
240 | ········} |
241 | |
242 | ········void TransformEquality(Expression ex1, Expression ex2) |
243 | ········{ |
244 | ············Transform( ex1 ); |
245 | ············TransformBinaryOperator( ExpressionType.Equal, ref ex2 ); |
246 | ············Transform( ex2 ); |
247 | ········} |
248 | |
249 | ········bool IsPropertyOfBaseExpression( Expression ex ) |
250 | ········{ |
251 | ············if (ex is MemberExpression pe) |
252 | ············{ |
253 | ················return pe.Expression == baseLeftSide; |
254 | ············} |
255 | |
256 | ············return false; |
257 | ········} |
258 | |
259 | ········void Transform( Expression ex ) |
260 | ········{ |
261 | ············expressionStack.Push( ex );··// This helps determining parent expressions |
262 | ············try |
263 | ············{ |
264 | ················if (ex == this.baseLeftSide) |
265 | ····················return; |
266 | ··················//------- |
267 | |
268 | ················string exStr = ex.ToString(); |
269 | ················if (exStr == "null") |
270 | ················{ |
271 | ····················sb.Append( "NULL" ); |
272 | ····················return; |
273 | ··················//------- |
274 | ················} |
275 | |
276 | ················if (ex.NodeType == ExpressionType.MemberAccess ) |
277 | ················{ |
278 | ····················// We try to compile the expression. |
279 | ····················// If it can be compiled, we have a value, |
280 | ····················// which should be added as a parameter. |
281 | ····················// We are safe to examine MemberAccess nodes only, |
282 | ····················// because local variables will be wrapped in |
283 | ····················// display classes and accessed as members of |
284 | ····················// these classes. All other cases are anyway |
285 | ····················// members of arbitrary types (like String.Empty). |
286 | ····················try |
287 | ····················{ |
288 | ························var d = Expression.Lambda( ex ).Compile(); |
289 | ························AddParameter( d.DynamicInvoke() ); |
290 | ························return; |
291 | ······················//------- |
292 | ····················} |
293 | ····················catch |
294 | ····················{··// Can't be compiled, so pass on. |
295 | ····················} |
296 | ················} |
297 | |
298 | ················BinaryExpression binex = ex as BinaryExpression; |
299 | ················if (binex != null) |
300 | ················{ |
301 | ····················Expression left = binex.Left; |
302 | ····················Expression right = binex.Right; |
303 | ····················if (left.NodeType == ExpressionType.Convert) |
304 | ························left = ( (UnaryExpression) left ).Operand; |
305 | ····················if (right.NodeType == ExpressionType.Convert) |
306 | ························right = ( (UnaryExpression) right ).Operand; |
307 | |
308 | ····················if (!IsPropertyOfBaseExpression( binex.Left ) && IsPropertyOfBaseExpression( binex.Right )) |
309 | ····················{ |
310 | ························Transform( FlipExpression( binex ) ); |
311 | ························return; |
312 | ························//------- |
313 | ····················} |
314 | |
315 | ····················int ownPrecedence = GetPrecedence(binex.NodeType); |
316 | ····················int childPrecedence = 0; |
317 | ····················BinaryExpression childBinex = binex.Left as BinaryExpression; |
318 | ····················bool leftbracket = false; |
319 | ····················if (childBinex != null) |
320 | ····················{ |
321 | ························childPrecedence = Math.Max( childPrecedence, GetPrecedence( childBinex.NodeType ) ); |
322 | ························leftbracket = childPrecedence > ownPrecedence; |
323 | ····················} |
324 | ····················childBinex = binex.Right as BinaryExpression; |
325 | ····················bool rightbracket = false; |
326 | ····················if (childBinex != null) |
327 | ····················{ |
328 | ························childPrecedence = Math.Max( childPrecedence, GetPrecedence( childBinex.NodeType ) ); |
329 | ························rightbracket = childPrecedence > ownPrecedence; |
330 | ····················} |
331 | ····················if (leftbracket) |
332 | ························sb.Append( '(' ); |
333 | ····················Transform( left ); |
334 | ····················if (leftbracket) |
335 | ························sb.Append( ')' ); |
336 | ····················TransformBinaryOperator( ex.NodeType, ref right ); |
337 | ····················if (rightbracket) |
338 | ························sb.Append( '(' ); |
339 | ····················Transform( right ); |
340 | ····················if (rightbracket) |
341 | ························sb.Append( ')' ); |
342 | ····················return; |
343 | ····················//------- |
344 | ················} |
345 | |
346 | ················MethodCallExpression mcex = ex as MethodCallExpression; |
347 | ················if (mcex != null) |
348 | ················{ |
349 | ····················var arguments = new List<Expression>(mcex.Arguments); |
350 | ····················string mname = mcex.Method.Name; |
351 | ····················if (mname == "op_Equality") |
352 | ····················{ |
353 | ························if (IsReversedExpression( arguments )) |
354 | ························{ |
355 | ····························FlipArguments( arguments ); |
356 | ························} |
357 | ························TransformEquality( arguments[0], arguments[1] ); |
358 | ····················} |
359 | ····················else if (mname == "Equals") |
360 | ····················{ |
361 | ························TransformEquality( mcex.Object, arguments[0] ); |
362 | ····················} |
363 | ····················else if (mname == "Like") |
364 | ····················{ |
365 | ························if (IsReversedExpression( arguments )) |
366 | ························{ |
367 | ····························FlipArguments( arguments ); |
368 | ························} |
369 | ························Transform( arguments[0] ); |
370 | ························sb.Append( " LIKE " ); |
371 | ························Transform( arguments[1] ); |
372 | ····················} |
373 | ····················else if (mname == "GreaterEqual") |
374 | ····················{ |
375 | ························string op = " >= "; |
376 | ························if (IsReversedExpression( arguments )) |
377 | ························{ |
378 | ····························FlipArguments( arguments ); |
379 | ····························op = " <= "; |
380 | ························} |
381 | ························Transform( arguments[0] ); |
382 | ························sb.Append( op ); |
383 | ························Transform( arguments[1] ); |
384 | ····················} |
385 | ····················else if (mname == "LowerEqual") |
386 | ····················{ |
387 | ························string op = " <= "; |
388 | ························if (IsReversedExpression( arguments )) |
389 | ························{ |
390 | ····························FlipArguments( arguments ); |
391 | ····························op = " >= "; |
392 | ························} |
393 | ························Transform( arguments[0] ); |
394 | ························sb.Append( op ); |
395 | ························Transform( arguments[1] ); |
396 | ····················} |
397 | ····················else if (mname == "GreaterThan") |
398 | ····················{ |
399 | ························string op = " > "; |
400 | ························if (IsReversedExpression( arguments )) |
401 | ························{ |
402 | ····························FlipArguments( arguments ); |
403 | ····························op = " < "; |
404 | ························} |
405 | ························Transform( arguments[0] ); |
406 | ························sb.Append( op ); |
407 | ························Transform( arguments[1] ); |
408 | ····················} |
409 | ····················else if (mname == "LowerThan") |
410 | ····················{ |
411 | ························string op = " < "; |
412 | ························if (IsReversedExpression( arguments )) |
413 | ························{ |
414 | ····························FlipArguments( arguments ); |
415 | ····························op = " > "; |
416 | ························} |
417 | ························Transform( arguments[0] ); |
418 | ························sb.Append( op ); |
419 | ························Transform( arguments[1] ); |
420 | ····················} |
421 | ····················else if (mname == "Between") |
422 | ····················{ |
423 | ························Transform( mcex.Arguments[0] ); |
424 | ························sb.Append( " BETWEEN " ); |
425 | ························Transform( mcex.Arguments[1] ); |
426 | ························sb.Append( " AND " ); |
427 | ························Transform( mcex.Arguments[2] ); |
428 | ····················} |
429 | ····················else if (mname == "get_Item") |
430 | ····················{ |
431 | ························string argStr = mcex.Arguments[0].ToString(); |
432 | ························if (argStr == "Any.Index") |
433 | ························{ |
434 | ····························Transform( mcex.Object ); |
435 | ····························return; |
436 | ····························//------- |
437 | ························} |
438 | ························Transform( mcex.Object ); |
439 | ························if (mcex.Object.Type.FullName == "NDO.ObjectId") |
440 | ························{ |
441 | ····························TransformOidIndex( mcex.Arguments[0] ); |
442 | ····························return; |
443 | ····························//------- |
444 | ························} |
445 | ························sb.Append( '(' ); |
446 | ························Transform( mcex.Arguments[0] ); |
447 | ························sb.Append( ')' ); |
448 | ····················} |
449 | ····················else if (mname == "ElementAt") |
450 | ····················{ |
451 | ························string argStr = mcex.Arguments[1].ToString(); |
452 | ························if (argStr == "Any.Index" || "Index.Any" == argStr) |
453 | ························{ |
454 | ····························Transform( mcex.Arguments[0] ); |
455 | ····························return; |
456 | ····························//------- |
457 | ························} |
458 | ························Transform( mcex.Arguments[0] ); |
459 | ························sb.Append( '(' ); |
460 | ························Transform( mcex.Arguments[1] ); |
461 | ························sb.Append( ')' ); |
462 | ····················} |
463 | ····················else if (mname == "Any") |
464 | ····················{ |
465 | ························Transform( mcex.Arguments[0] ); |
466 | ························var exprx = mcex.Arguments[1]; |
467 | ························if (sb[sb.Length - 1] != '.') |
468 | ····························sb.Append( '.' ); |
469 | ························Transform( ( (LambdaExpression) mcex.Arguments[1] ).Body ); |
470 | ····················} |
471 | ····················else if (mname == "In") |
472 | ····················{ |
473 | ························var arg = mcex.Arguments[1]; |
474 | ························IEnumerable list = (IEnumerable)(Expression.Lambda(arg).Compile().DynamicInvoke()); |
475 | ························Transform( mcex.Arguments[0] ); |
476 | ························sb.Append( " IN (" ); |
477 | ························var en = list.GetEnumerator(); |
478 | ························en.MoveNext(); |
479 | ························bool doQuote = en.Current is string || en.Current is Guid || en.Current is DateTime; |
480 | foreach ( object item in list) |
481 | ························{ |
482 | ····························var obj = item; |
483 | ····························if (obj is string s) |
484 | ····························{ |
485 | ································obj = s.Replace( "'", "''" ); |
486 | ····························} |
487 | ····························if (doQuote) |
488 | ································sb.Append( '\'' ); |
489 | ····························sb.Append( obj ); |
490 | ····························if (doQuote) |
491 | ································sb.Append( '\'' ); |
492 | ····························sb.Append( ',' ); |
493 | ························} |
494 | ························sb.Length -= 1; |
495 | ························sb.Append( ')' ); |
496 | ····················} |
497 | ····················else if (mname == "Oid") |
498 | ····················{ |
499 | ························var sbLength = sb.Length; |
500 | ························Transform( mcex.Arguments[0] ); |
501 | ························if (sb.Length > sbLength) |
502 | ····························sb.Append( ".oid" ); |
503 | ························else |
504 | ····························sb.Append( "oid" ); |
505 | ························if (mcex.Arguments.Count > 1) |
506 | ························{ |
507 | ····························TransformOidIndex( mcex.Arguments[1] ); |
508 | ························} |
509 | ····················} |
510 | ····················else··// Assume, we have a server function here |
511 | ····················{ |
512 | ························var method = mcex.Method; |
513 | ························var attrs = method.GetCustomAttributes( typeof( ServerFunctionAttribute ), true ); |
514 | ························if (attrs.Length > 0) |
515 | ····························mname = ((ServerFunctionAttribute) attrs[0]).Name; |
516 | ························sb.Append( mname ); |
517 | ························sb.Append( '(' ); |
518 | ························var end = arguments.Count - 1; |
519 | ························var i = 0; |
520 | ························foreach (var arg in arguments) |
521 | ························{ |
522 | ····························Transform( arg ); |
523 | ····························if (i < end) |
524 | ································sb.Append( ", " ); |
525 | ····························i++; |
526 | ························} |
527 | ························sb.Append( ')' ); |
528 | ····················} |
529 | ····················return; |
530 | ····················//------- |
531 | ················} |
532 | |
533 | ················if (ex.NodeType == ExpressionType.MemberAccess) |
534 | ················{ |
535 | ····················MemberExpression memberex = (MemberExpression) ex; |
536 | ····················if (memberex.Member.Name == "NDOObjectId") |
537 | ····················{ |
538 | ························if (memberex.Expression.ToString() != this.baseParameterName) |
539 | ························{ |
540 | ····························Transform( memberex.Expression ); |
541 | ····························if (sb[sb.Length - 1] != '.') |
542 | ································sb.Append( '.' ); |
543 | ························} |
544 | ························sb.Append( "oid" ); |
545 | ························return; |
546 | ····················} |
547 | ····················if (ex.Type == typeof( bool )) |
548 | ····················{ |
549 | ························var top = expressionStack.Pop(); |
550 | ························bool transformIt = expressionStack.Count == 0;··// The boolean expression is the top |
551 | ························if (expressionStack.Count > 0) |
552 | ························{ |
553 | ····························var parent = expressionStack.Peek(); |
554 | ····························if (parent.NodeType != ExpressionType.Equal && parent.NodeType != ExpressionType.NotEqual) |
555 | ····························{ |
556 | ································// A Boolean Expression, which is not part of an Equals or NotEquals expression, |
557 | ································// must be unary. Since Sql Server doesn't support unary boolean expressions, we add an Equals Expression which compares with 1. |
558 | ································// Fortunately this works with other databases, too. |
559 | ································transformIt = true; |
560 | ····························} |
561 | ························} |
562 | ························if (transformIt) |
563 | ························{ |
564 | ····························sb.Append( memberex.Member.Name ); |
565 | ····························sb.Append( " = 1" ); |
566 | ························} |
567 | ························expressionStack.Push( top ); |
568 | ························if (transformIt) |
569 | ····························return; |
570 | ························//------- |
571 | ····················} |
572 | ····················if (memberex.Expression != baseLeftSide) |
573 | ····················{ |
574 | ························// This happens while navigating through relations. |
575 | ························Transform( memberex.Expression ); |
576 | ························if (sb[sb.Length - 1] != '.') |
577 | ····························sb.Append( '.' ); |
578 | ····················} |
579 | ····················sb.Append( memberex.Member.Name ); |
580 | ····················return; |
581 | ····················//------- |
582 | ················} |
583 | |
584 | ················else if (ex.NodeType == ExpressionType.Constant) |
585 | ················{ |
586 | ····················ConstantExpression constEx = (ConstantExpression) ex; |
587 | ····················AddParameter( constEx.Value ); |
588 | ····················return; |
589 | ····················//------- |
590 | ················} |
591 | |
592 | ················else if (ex.NodeType == ExpressionType.Convert) |
593 | ················{ |
594 | ····················Transform( ( (UnaryExpression) ex ).Operand ); |
595 | ····················return; |
596 | ····················//------- |
597 | ················} |
598 | |
599 | ················else if (ex.NodeType == ExpressionType.Not) |
600 | ················{ |
601 | ····················sb.Append( " NOT " ); |
602 | ····················Expression inner = ((UnaryExpression)ex).Operand; |
603 | ····················var binary = (inner is BinaryExpression); |
604 | ····················if (binary) |
605 | ························sb.Append( '(' ); |
606 | ····················Transform( inner ); |
607 | ····················if (binary) |
608 | ························sb.Append( ')' ); |
609 | ····················return; |
610 | ····················//------- |
611 | ················} |
612 | ············} |
613 | ············finally |
614 | ············{················ |
615 | ················expressionStack.Pop(); |
616 | ············} |
617 | ········} |
618 | |
619 | ········private void TransformEquality( List<Expression> arguments ) |
620 | ········{ |
621 | ············Transform( arguments[0] ); |
622 | ············sb.Append( " = " ); |
623 | ············Transform( arguments[1] ); |
624 | ········} |
625 | |
626 | ········private void TransformOidIndex( Expression ex ) |
627 | ········{ |
628 | ············var ce = ex as ConstantExpression; |
629 | ············if (ce == null) |
630 | ················throw new Exception( "Oid index expression must be a ConstantExpression" ); |
631 | ············if (!( ce.Value is System.Int32 )) |
632 | ················throw new Exception( "Oid index expression must be an Int32" ); |
633 | |
634 | ············if ((int) ce.Value != 0) |
635 | ············{ |
636 | ················sb.Append( '(' ); |
637 | ················sb.Append( ce.Value ); |
638 | ················sb.Append( ')' ); |
639 | ············} |
640 | ········} |
641 | ····} |
642 | } |