Datei: NDODLL/Linq/ExpressionTreeTransformer.cs
Last Commit (397e4c7)
| 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 | } |
New Commit (a366466)
| 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, "&", 4) , |
| 69 | ············new OperatorEntry(ExpressionType.AndAlso, "AND", 4), |
| 70 | new OperatorEntry( ExpressionType. 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 | } |