ASP.NET MVC 3 直到我膝盖中了一箭【11】MVC3MusicStore排序
1.~/Resources/下添加资源文件Resource
2.~/Views/StoreManager/下更新视图Index
1 @model IEnumerable<MVC3MusicStore.Models.Album> 2 @helper Truncate(string input, int length) 3 { 4 if (input.Length <= length) 5 { 6 @input 7 } 8 else 9 {10 @input.Substring(0, length)<text>...</text>11 12 }13 }14 @{15 ViewBag.Title = "Index";16 }17 @using MVC3MusicStore.Helpers18 <h2>19 Index</h2>20 <p>21 @Html.ActionLink((string)ViewBag.CreateLink, "Create")22 </p>23 <table>24 <tr>25 <th>26 @Html.ActionLink((string)ViewBag.GenreDisplay, "Index", new { sortOrder = ViewBag.GenreSortParam })27 </th>28 <th>29 @Html.ActionLink((string)ViewBag.ArtistDisplay, "Index", new { sortOrder = ViewBag.ArtistSortParam })30 </th>31 <th>32 @Html.ActionLink((string)ViewBag.TitleDisplay, "Index", new { sortOrder = ViewBag.TitleSortParam })33 </th>34 <th>35 @Html.ActionLink((string)ViewBag.PriceDisplay, "Index", new { sortOrder = ViewBag.PriceSortParam })36 </th>37 @* <th>38 AlbumArtUrl39 </th>*@40 <th>41 </th>42 </tr>43 @foreach (var item in Model)44 {45 <tr>46 <td>47 @*@item.GenreId*@48 @Html.DisplayFor(modelItem => item.Genre.Name)49 </td>50 <td>51 @*@item.ArtistId*@ @*@Html.DisplayFor(modelItem => item.Artist.Name)*@52 @Truncate(item.Artist.Name, 25)53 </td>54 <td>55 @*@item.Title*@56 @Truncate(item.Title, 25)57 </td>58 <td>59 @*@String.Format("{0:F}", item.Price)*@60 @Html.DisplayFor(modelItem => item.Price)61 </td>62 @* <td>63 @item.AlbumArtUrl64 </td>*@65 <td>66 @Html.ActionLink((string)ViewBag.EditLink, "Edit", new { id = item.AlbumId })|67 @Html.ActionLink((string)ViewBag.DetailsLink, "Details", new { id = item.AlbumId })|68 @Html.ActionLink((string)ViewBag.DeleteLink, "Delete", new { id = item.AlbumId })69 </td>70 </tr>71 }72 </table>
3.使用LINQ Dynamic Query Library
将DynamicQuery文件夹中的Dynamic源文件copy至项目中,添加命名空间"using System.Linq.Dynamic",这样你就可以使用它了。
1 //Copyright (C) Microsoft Corporation. All rights reserved. 2 3 using System; 4 using System.Collections.Generic; 5 using System.Text; 6 using System.Linq; 7 using System.Linq.Expressions; 8 using System.Reflection; 9 using System.Reflection.Emit; 10 using System.Threading; 11 12 namespace System.Linq.Dynamic 13 { 14 public static class DynamicQueryable 15 { 16 public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) { 17 return (IQueryable<T>)Where((IQueryable)source, predicate, values); 18 } 19 20 public static IQueryable Where(this IQueryable source, string predicate, params object[] values) { 21 if (source == null) throw new ArgumentNullException("source"); 22 if (predicate == null) throw new ArgumentNullException("predicate"); 23 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values); 24 return source.Provider.CreateQuery( 25 Expression.Call( 26 typeof(Queryable), "Where", 27 new Type[] { source.ElementType }, 28 source.Expression, Expression.Quote(lambda))); 29 } 30 31 public static IQueryable Select(this IQueryable source, string selector, params object[] values) { 32 if (source == null) throw new ArgumentNullException("source"); 33 if (selector == null) throw new ArgumentNullException("selector"); 34 LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values); 35 return source.Provider.CreateQuery( 36 Expression.Call( 37 typeof(Queryable), "Select", 38 new Type[] { source.ElementType, lambda.Body.Type }, 39 source.Expression, Expression.Quote(lambda))); 40 } 41 42 public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) { 43 return (IQueryable<T>)OrderBy((IQueryable)source, ordering, values); 44 } 45 46 public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values) { 47 if (source == null) throw new ArgumentNullException("source"); 48 if (ordering == null) throw new ArgumentNullException("ordering"); 49 ParameterExpression[] parameters = new ParameterExpression[] { 50 Expression.Parameter(source.ElementType, "") }; 51 ExpressionParser parser = new ExpressionParser(parameters, ordering, values); 52 IEnumerable<DynamicOrdering> orderings = parser.ParseOrdering(); 53 Expression queryExpr = source.Expression; 54 string methodAsc = "OrderBy"; 55 string methodDesc = "OrderByDescending"; 56 foreach (DynamicOrdering o in orderings) { 57 queryExpr = Expression.Call( 58 typeof(Queryable), o.Ascending ? methodAsc : methodDesc, 59 new Type[] { source.ElementType, o.Selector.Type }, 60 queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters))); 61 methodAsc = "ThenBy"; 62 methodDesc = "ThenByDescending"; 63 } 64 return source.Provider.CreateQuery(queryExpr); 65 } 66 67 public static IQueryable Take(this IQueryable source, int count) { 68 if (source == null) throw new ArgumentNullException("source"); 69 return source.Provider.CreateQuery( 70 Expression.Call( 71 typeof(Queryable), "Take", 72 new Type[] { source.ElementType }, 73 source.Expression, Expression.Constant(count))); 74 } 75 76 public static IQueryable Skip(this IQueryable source, int count) { 77 if (source == null) throw new ArgumentNullException("source"); 78 return source.Provider.CreateQuery( 79 Expression.Call( 80 typeof(Queryable), "Skip", 81 new Type[] { source.ElementType }, 82 source.Expression, Expression.Constant(count))); 83 } 84 85 public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values) { 86 if (source == null) throw new ArgumentNullException("source"); 87 if (keySelector == null) throw new ArgumentNullException("keySelector"); 88 if (elementSelector == null) throw new ArgumentNullException("elementSelector"); 89 LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values); 90 LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values); 91 return source.Provider.CreateQuery( 92 Expression.Call( 93 typeof(Queryable), "GroupBy", 94 new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type }, 95 source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))); 96 } 97 98 public static bool Any(this IQueryable source) { 99 if (source == null) throw new ArgumentNullException("source"); 100 return (bool)source.Provider.Execute( 101 Expression.Call( 102 typeof(Queryable), "Any", 103 new Type[] { source.ElementType }, source.Expression)); 104 } 105 106 public static int Count(this IQueryable source) { 107 if (source == null) throw new ArgumentNullException("source"); 108 return (int)source.Provider.Execute( 109 Expression.Call( 110 typeof(Queryable), "Count", 111 new Type[] { source.ElementType }, source.Expression)); 112 } 113 } 114 115 public abstract class DynamicClass 116 { 117 public override string ToString() { 118 PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 119 StringBuilder sb = new StringBuilder(); 120 sb.Append("{"); 121 for (int i = 0; i < props.Length; i++) { 122 if (i > 0) sb.Append(", "); 123 sb.Append(props[i].Name); 124 sb.Append("="); 125 sb.Append(props[i].GetValue(this, null)); 126 } 127 sb.Append("}"); 128 return sb.ToString(); 129 } 130 } 131 132 public class DynamicProperty 133 { 134 string name; 135 Type type; 136 137 public DynamicProperty(string name, Type type) { 138 if (name == null) throw new ArgumentNullException("name"); 139 if (type == null) throw new ArgumentNullException("type"); 140 this.name = name; 141 this.type = type; 142 } 143 144 public string Name { 145 get { return name; } 146 } 147 148 public Type Type { 149 get { return type; } 150 } 151 } 152 153 public static class DynamicExpression 154 { 155 public static Expression Parse(Type resultType, string expression, params object[] values) { 156 ExpressionParser parser = new ExpressionParser(null, expression, values); 157 return parser.Parse(resultType); 158 } 159 160 public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values) { 161 return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values); 162 } 163 164 public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) { 165 ExpressionParser parser = new ExpressionParser(parameters, expression, values); 166 return Expression.Lambda(parser.Parse(resultType), parameters); 167 } 168 169 public static Expression<Func<T, S>> ParseLambda<T, S>(string expression, params object[] values) { 170 return (Expression<Func<T, S>>)ParseLambda(typeof(T), typeof(S), expression, values); 171 } 172 173 public static Type CreateClass(params DynamicProperty[] properties) { 174 return ClassFactory.Instance.GetDynamicClass(properties); 175 } 176 177 public static Type CreateClass(IEnumerable<DynamicProperty> properties) { 178 return ClassFactory.Instance.GetDynamicClass(properties); 179 } 180 } 181 182 internal class DynamicOrdering 183 { 184 public Expression Selector; 185 public bool Ascending; 186 } 187 188 internal class Signature : IEquatable<Signature> 189 { 190 public DynamicProperty[] properties; 191 public int hashCode; 192 193 public Signature(IEnumerable<DynamicProperty> properties) { 194 this.properties = properties.ToArray(); 195 hashCode = 0; 196 foreach (DynamicProperty p in properties) { 197 hashCode ^= p.Name.GetHashCode() ^ p.Type.GetHashCode(); 198 } 199 } 200 201 public override int GetHashCode() { 202 return hashCode; 203 } 204 205 public override bool Equals(object obj) { 206 return obj is Signature ? Equals((Signature)obj) : false; 207 } 208 209 public bool Equals(Signature other) { 210 if (properties.Length != other.properties.Length) return false; 211 for (int i = 0; i < properties.Length; i++) { 212 if (properties[i].Name != other.properties[i].Name || 213 properties[i].Type != other.properties[i].Type) return false; 214 } 215 return true; 216 } 217 } 218 219 internal class ClassFactory 220 { 221 public static readonly ClassFactory Instance = new ClassFactory(); 222 223 static ClassFactory() { } // Trigger lazy initialization of static fields 224 225 ModuleBuilder module; 226 Dictionary<Signature, Type> classes; 227 int classCount; 228 ReaderWriterLock rwLock; 229 230 private ClassFactory() { 231 AssemblyName name = new AssemblyName("DynamicClasses"); 232 AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); 233 #if ENABLE_LINQ_PARTIAL_TRUST 234 new ReflectionPermission(PermissionState.Unrestricted).Assert(); 235 #endif 236 try { 237 module = assembly.DefineDynamicModule("Module"); 238 } 239 finally { 240 #if ENABLE_LINQ_PARTIAL_TRUST 241 PermissionSet.RevertAssert(); 242 #endif 243 } 244 classes = new Dictionary<Signature, Type>(); 245 rwLock = new ReaderWriterLock(); 246 } 247 248 public Type GetDynamicClass(IEnumerable<DynamicProperty> properties) { 249 rwLock.AcquireReaderLock(Timeout.Infinite); 250 try { 251 Signature signature = new Signature(properties); 252 Type type; 253 if (!classes.TryGetValue(signature, out type)) { 254 type = CreateDynamicClass(signature.properties); 255 classes.Add(signature, type); 256 } 257 return type; 258 } 259 finally { 260 rwLock.ReleaseReaderLock(); 261 } 262 } 263 264 Type CreateDynamicClass(DynamicProperty[] properties) { 265 LockCookie cookie = rwLock.UpgradeToWriterLock(Timeout.Infinite); 266 try { 267 string typeName = "DynamicClass" + (classCount + 1); 268 #if ENABLE_LINQ_PARTIAL_TRUST 269 new ReflectionPermission(PermissionState.Unrestricted).Assert(); 270 #endif 271 try { 272 TypeBuilder tb = this.module.DefineType(typeName, TypeAttributes.Class | 273 TypeAttributes.Public, typeof(DynamicClass)); 274 FieldInfo[] fields = GenerateProperties(tb, properties); 275 GenerateEquals(tb, fields); 276 GenerateGetHashCode(tb, fields); 277 Type result = tb.CreateType(); 278 classCount++; 279 return result; 280 } 281 finally { 282 #if ENABLE_LINQ_PARTIAL_TRUST 283 PermissionSet.RevertAssert(); 284 #endif 285 } 286 } 287 finally { 288 rwLock.DowngradeFromWriterLock(ref cookie); 289 } 290 } 291 292 FieldInfo[] GenerateProperties(TypeBuilder tb, DynamicProperty[] properties) { 293 FieldInfo[] fields = new FieldBuilder[properties.Length]; 294 for (int i = 0; i < properties.Length; i++) { 295 DynamicProperty dp = properties[i]; 296 FieldBuilder fb = tb.DefineField("_" + dp.Name, dp.Type, FieldAttributes.Private); 297 PropertyBuilder pb = tb.DefineProperty(dp.Name, PropertyAttributes.HasDefault, dp.Type, null); 298 MethodBuilder mbGet = tb.DefineMethod("get_" + dp.Name, 299 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 300 dp.Type, Type.EmptyTypes); 301 ILGenerator genGet = mbGet.GetILGenerator(); 302 genGet.Emit(OpCodes.Ldarg_0); 303 genGet.Emit(OpCodes.Ldfld, fb); 304 genGet.Emit(OpCodes.Ret); 305 MethodBuilder mbSet = tb.DefineMethod("set_" + dp.Name, 306 MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 307 null, new Type[] { dp.Type }); 308 ILGenerator genSet = mbSet.GetILGenerator(); 309 genSet.Emit(OpCodes.Ldarg_0); 310 genSet.Emit(OpCodes.Ldarg_1); 311 genSet.Emit(OpCodes.Stfld, fb); 312 genSet.Emit(OpCodes.Ret); 313 pb.SetGetMethod(mbGet); 314 pb.SetSetMethod(mbSet); 315 fields[i] = fb; 316 } 317 return fields; 318 } 319 320 void GenerateEquals(TypeBuilder tb, FieldInfo[] fields) { 321 MethodBuilder mb = tb.DefineMethod("Equals", 322 MethodAttributes.Public | MethodAttributes.ReuseSlot | 323 MethodAttributes.Virtual | MethodAttributes.HideBySig, 324 typeof(bool), new Type[] { typeof(object) }); 325 ILGenerator gen = mb.GetILGenerator(); 326 LocalBuilder other = gen.DeclareLocal(tb); 327 Label next = gen.DefineLabel(); 328 gen.Emit(OpCodes.Ldarg_1); 329 gen.Emit(OpCodes.Isinst, tb); 330 gen.Emit(OpCodes.Stloc, other); 331 gen.Emit(OpCodes.Ldloc, other); 332 gen.Emit(OpCodes.Brtrue_S, next); 333 gen.Emit(OpCodes.Ldc_I4_0); 334 gen.Emit(OpCodes.Ret); 335 gen.MarkLabel(next); 336 foreach (FieldInfo field in fields) { 337 Type ft = field.FieldType; 338 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); 339 next = gen.DefineLabel(); 340 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); 341 gen.Emit(OpCodes.Ldarg_0); 342 gen.Emit(OpCodes.Ldfld, field); 343 gen.Emit(OpCodes.Ldloc, other); 344 gen.Emit(OpCodes.Ldfld, field); 345 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null); 346 gen.Emit(OpCodes.Brtrue_S, next); 347 gen.Emit(OpCodes.Ldc_I4_0); 348 gen.Emit(OpCodes.Ret); 349 gen.MarkLabel(next); 350 } 351 gen.Emit(OpCodes.Ldc_I4_1); 352 gen.Emit(OpCodes.Ret); 353 } 354 355 void GenerateGetHashCode(TypeBuilder tb, FieldInfo[] fields) { 356 MethodBuilder mb = tb.DefineMethod("GetHashCode", 357 MethodAttributes.Public | MethodAttributes.ReuseSlot | 358 MethodAttributes.Virtual | MethodAttributes.HideBySig, 359 typeof(int), Type.EmptyTypes); 360 ILGenerator gen = mb.GetILGenerator(); 361 gen.Emit(OpCodes.Ldc_I4_0); 362 foreach (FieldInfo field in fields) { 363 Type ft = field.FieldType; 364 Type ct = typeof(EqualityComparer<>).MakeGenericType(ft); 365 gen.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null); 366 gen.Emit(OpCodes.Ldarg_0); 367 gen.Emit(OpCodes.Ldfld, field); 368 gen.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null); 369 gen.Emit(OpCodes.Xor); 370 } 371 gen.Emit(OpCodes.Ret); 372 } 373 } 374 375 public sealed class ParseException : Exception 376 { 377 int position; 378 379 public ParseException(string message, int position) 380 : base(message) { 381 this.position = position; 382 } 383 384 public int Position { 385 get { return position; } 386 } 387 388 public override string ToString() { 389 return string.Format(Res.ParseExceptionFormat, Message, position); 390 } 391 } 392 393 internal class ExpressionParser 394 { 395 struct Token 396 { 397 public TokenId id; 398 public string text; 399 public int pos; 400 } 401 402 enum TokenId 403 { 404 Unknown, 405 End, 406 Identifier, 407 StringLiteral, 408 IntegerLiteral, 409 RealLiteral, 410 Exclamation, 411 Percent, 412 Amphersand, 413 OpenParen, 414 CloseParen, 415 Asterisk, 416 Plus, 417 Comma, 418 Minus, 419 Dot, 420 Slash, 421 Colon, 422 LessThan, 423 Equal, 424 GreaterThan, 425 Question, 426 OpenBracket, 427 CloseBracket, 428 Bar, 429 ExclamationEqual, 430 DoubleAmphersand, 431 LessThanEqual, 432 LessGreater, 433 DoubleEqual, 434 GreaterThanEqual, 435 DoubleBar 436 } 437 438 interface ILogicalSignatures 439 { 440 void F(bool x, bool y); 441 void F(bool? x, bool? y); 442 } 443 444 interface IArithmeticSignatures 445 { 446 void F(int x, int y); 447 void F(uint x, uint y); 448 void F(long x, long y); 449 void F(ulong x, ulong y); 450 void F(float x, float y); 451 void F(double x, double y); 452 void F(decimal x, decimal y); 453 void F(int? x, int? y); 454 void F(uint? x, uint? y); 455 void F(long? x, long? y); 456 void F(ulong? x, ulong? y); 457 void F(float? x, float? y); 458 void F(double? x, double? y); 459 void F(decimal? x, decimal? y); 460 } 461 462 interface IRelationalSignatures : IArithmeticSignatures 463 { 464 void F(string x, string y); 465 void F(char x, char y); 466 void F(DateTime x, DateTime y); 467 void F(TimeSpan x, TimeSpan y); 468 void F(char? x, char? y); 469 void F(DateTime? x, DateTime? y); 470 void F(TimeSpan? x, TimeSpan? y); 471 } 472 473 interface IEqualitySignatures : IRelationalSignatures 474 { 475 void F(bool x, bool y); 476 void F(bool? x, bool? y); 477 } 478 479 interface IAddSignatures : IArithmeticSignatures 480 { 481 void F(DateTime x, TimeSpan y); 482 void F(TimeSpan x, TimeSpan y); 483 void F(DateTime? x, TimeSpan? y); 484 void F(TimeSpan? x, TimeSpan? y); 485 } 486 487 interface ISubtractSignatures : IAddSignatures 488 { 489 void F(DateTime x, DateTime y); 490 void F(DateTime? x, DateTime? y); 491 } 492 493 interface INegationSignatures 494 { 495 void F(int x); 496 void F(long x); 497 void F(float x); 498 void F(double x); 499 void F(decimal x); 500 void F(int? x); 501 void F(long? x); 502 void F(float? x); 503 void F(double? x); 504 void F(decimal? x); 505 } 506 507 interface INotSignatures 508 { 509 void F(bool x); 510 void F(bool? x); 511 } 512 513 interface IEnumerableSignatures 514 { 515 void Where(bool predicate); 516 void Any(); 517 void Any(bool predicate); 518 void All(bool predicate); 519 void Count(); 520 void Count(bool predicate); 521 void Min(object selector); 522 void Max(object selector); 523 void Sum(int selector); 524 void Sum(int? selector); 525 void Sum(long selector); 526 void Sum(long? selector); 527 void Sum(float selector); 528 void Sum(float? selector); 529 void Sum(double selector); 530 void Sum(double? selector); 531 void Sum(decimal selector); 532 void Sum(decimal? selector); 533 void Average(int selector); 534 void Average(int? selector); 535 void Average(long selector); 536 void Average(long? selector); 537 void Average(float selector); 538 void Average(float? selector); 539 void Average(double selector); 540 void Average(double? selector); 541 void Average(decimal selector); 542 void Average(decimal? selector); 543 } 544 545 static readonly Type[] predefinedTypes = { 546 typeof(Object), 547 typeof(Boolean), 548 typeof(Char), 549 typeof(String), 550 typeof(SByte), 551 typeof(Byte), 552 typeof(Int16), 553 typeof(UInt16), 554 typeof(Int32), 555 typeof(UInt32), 556 typeof(Int64), 557 typeof(UInt64), 558 typeof(Single), 559 typeof(Double), 560 typeof(Decimal), 561 typeof(DateTime), 562 typeof(TimeSpan), 563 typeof(Guid), 564 typeof(Math), 565 typeof(Convert) 566 }; 567 568 static readonly Expression trueLiteral = Expression.Constant(true); 569 static readonly Expression falseLiteral = Expression.Constant(false); 570 static readonly Expression nullLiteral = Expression.Constant(null); 571 572 static readonly string keywordIt = "it"; 573 static readonly string keywordIif = "iif"; 574 static readonly string keywordNew = "new"; 575 576 static Dictionary<string, object> keywords; 577 578 Dictionary<string, object> symbols; 579 IDictionary<string, object> externals; 580 Dictionary<Expression, string> literals; 581 ParameterExpression it; 582 string text; 583 int textPos; 584 int textLen; 585 char ch; 586 Token token; 587 588 public ExpressionParser(ParameterExpression[] parameters, string expression, object[] values) { 589 if (expression == null) throw new ArgumentNullException("expression"); 590 if (keywords == null) keywords = CreateKeywords(); 591 symbols = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); 592 literals = new Dictionary<Expression, string>(); 593 if (parameters != null) ProcessParameters(parameters); 594 if (values != null) ProcessValues(values); 595 text = expression; 596 textLen = text.Length; 597 SetTextPos(0); 598 NextToken(); 599 } 600 601 void ProcessParameters(ParameterExpression[] parameters) { 602 foreach (ParameterExpression pe in parameters) 603 if (!String.IsNullOrEmpty(pe.Name)) 604 AddSymbol(pe.Name, pe); 605 if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name)) 606 it = parameters[0]; 607 } 608 609 void ProcessValues(object[] values) { 610 for (int i = 0; i < values.Length; i++) { 611 object value = values[i]; 612 if (i == values.Length - 1 && value is IDictionary<string, object>) { 613 externals = (IDictionary<string, object>)value; 614 } 615 else { 616 AddSymbol("@" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), value); 617 } 618 } 619 } 620 621 void AddSymbol(string name, object value) { 622 if (symbols.ContainsKey(name)) 623 throw ParseError(Res.DuplicateIdentifier, name); 624 symbols.Add(name, value); 625 } 626 627 public Expression Parse(Type resultType) { 628 int exprPos = token.pos; 629 Expression expr = ParseExpression(); 630 if (resultType != null) 631 if ((expr = PromoteExpression(expr, resultType, true)) == null) 632 throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType)); 633 ValidateToken(TokenId.End, Res.SyntaxError); 634 return expr; 635 } 636 637 #pragma warning disable 0219 638 public IEnumerable<DynamicOrdering> ParseOrdering() { 639 List<DynamicOrdering> orderings = new List<DynamicOrdering>(); 640 while (true) { 641 Expression expr = ParseExpression(); 642 bool ascending = true; 643 if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending")) { 644 NextToken(); 645 } 646 else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending")) { 647 NextToken(); 648 ascending = false; 649 } 650 orderings.Add(new DynamicOrdering { Selector = expr, Ascending = ascending }); 651 if (token.id != TokenId.Comma) break; 652 NextToken(); 653 } 654 ValidateToken(TokenId.End, Res.SyntaxError); 655 return orderings; 656 } 657 #pragma warning restore 0219 658 659 // ?: operator 660 Expression ParseExpression() { 661 int errorPos = token.pos; 662 Expression expr = ParseLogicalOr(); 663 if (token.id == TokenId.Question) { 664 NextToken(); 665 Expression expr1 = ParseExpression(); 666 ValidateToken(TokenId.Colon, Res.ColonExpected); 667 NextToken(); 668 Expression expr2 = ParseExpression(); 669 expr = GenerateConditional(expr, expr1, expr2, errorPos); 670 } 671 return expr; 672 } 673 674 // ||, or operator 675 Expression ParseLogicalOr() { 676 Expression left = ParseLogicalAnd(); 677 while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or")) { 678 Token op = token; 679 NextToken(); 680 Expression right = ParseLogicalAnd(); 681 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); 682 left = Expression.OrElse(left, right); 683 } 684 return left; 685 } 686 687 // &&, and operator 688 Expression ParseLogicalAnd() { 689 Expression left = ParseComparison(); 690 while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and")) { 691 Token op = token; 692 NextToken(); 693 Expression right = ParseComparison(); 694 CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); 695 left = Expression.AndAlso(left, right); 696 } 697 return left; 698 } 699 700 // =, ==, !=, <>, >, >=, <, <= operators 701 Expression ParseComparison() { 702 Expression left = ParseAdditive(); 703 while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual || 704 token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater || 705 token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual || 706 token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual) { 707 Token op = token; 708 NextToken(); 709 Expression right = ParseAdditive(); 710 bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual || 711 op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater; 712 if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType) { 713 if (left.Type != right.Type) { 714 if (left.Type.IsAssignableFrom(right.Type)) { 715 right = Expression.Convert(right, left.Type); 716 } 717 else if (right.Type.IsAssignableFrom(left.Type)) { 718 left = Expression.Convert(left, right.Type); 719 } 720 else { 721 throw IncompatibleOperandsError(op.text, left, right, op.pos); 722 } 723 } 724 } 725 else if (IsEnumType(left.Type) || IsEnumType(right.Type)) { 726 if (left.Type != right.Type) { 727 Expression e; 728 if ((e = PromoteExpression(right, left.Type, true)) != null) { 729 right = e; 730 } 731 else if ((e = PromoteExpression(left, right.Type, true)) != null) { 732 left = e; 733 } 734 else { 735 throw IncompatibleOperandsError(op.text, left, right, op.pos); 736 } 737 } 738 } 739 else { 740 CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures), 741 op.text, ref left, ref right, op.pos); 742 } 743 switch (op.id) { 744 case TokenId.Equal: 745 case TokenId.DoubleEqual: 746 left = GenerateEqual(left, right); 747 break; 748 case TokenId.ExclamationEqual: 749 case TokenId.LessGreater: 750 left = GenerateNotEqual(left, right); 751 break; 752 case TokenId.GreaterThan: 753 left = GenerateGreaterThan(left, right); 754 break; 755 case TokenId.GreaterThanEqual: 756 left = GenerateGreaterThanEqual(left, right); 757 break; 758 case TokenId.LessThan: 759 left = GenerateLessThan(left, right); 760 break; 761 case TokenId.LessThanEqual: 762 left = GenerateLessThanEqual(left, right); 763 break; 764 } 765 } 766 return left; 767 } 768 769 // +, -, & operators 770 Expression ParseAdditive() { 771 Expression left = ParseMultiplicative(); 772 while (token.id == TokenId.Plus || token.id == TokenId.Minus || 773 token.id == TokenId.Amphersand) { 774 Token op = token; 775 NextToken(); 776 Expression right = ParseMultiplicative(); 777 switch (op.id) { 778 case TokenId.Plus: 779 if (left.Type == typeof(string) || right.Type == typeof(string)) 780 goto case TokenId.Amphersand; 781 CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos); 782 left = GenerateAdd(left, right); 783 break; 784 case TokenId.Minus: 785 CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos); 786 left = GenerateSubtract(left, right); 787 break; 788 case TokenId.Amphersand: 789 left = GenerateStringConcat(left, right); 790 break; 791 } 792 } 793 return left; 794 } 795 796 // *, /, %, mod operators 797 Expression ParseMultiplicative() { 798 Expression left = ParseUnary(); 799 while (token.id == TokenId.Asterisk || token.id == TokenId.Slash || 800 token.id == TokenId.Percent || TokenIdentifierIs("mod")) { 801 Token op = token; 802 NextToken(); 803 Expression right = ParseUnary(); 804 CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos); 805 switch (op.id) { 806 case TokenId.Asterisk: 807 left = Expression.Multiply(left, right); 808 break; 809 case TokenId.Slash: 810 left = Expression.Divide(left, right); 811 break; 812 case TokenId.Percent: 813 case TokenId.Identifier: 814 left = Expression.Modulo(left, right); 815 break; 816 } 817 } 818 return left; 819 } 820 821 // -, !, not unary operators 822 Expression ParseUnary() { 823 if (token.id == TokenId.Minus || token.id == TokenId.Exclamation || 824 TokenIdentifierIs("not")) { 825 Token op = token; 826 NextToken(); 827 if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral || 828 token.id == TokenId.RealLiteral)) { 829 token.text = "-" + token.text; 830 token.pos = op.pos; 831 return ParsePrimary(); 832 } 833 Expression expr = ParseUnary(); 834 if (op.id == TokenId.Minus) { 835 CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos); 836 expr = Expression.Negate(expr); 837 } 838 else { 839 CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos); 840 expr = Expression.Not(expr); 841 } 842 return expr; 843 } 844 return ParsePrimary(); 845 } 846 847 Expression ParsePrimary() { 848 Expression expr = ParsePrimaryStart(); 849 while (true) { 850 if (token.id == TokenId.Dot) { 851 NextToken(); 852 expr = ParseMemberAccess(null, expr); 853 } 854 else if (token.id == TokenId.OpenBracket) { 855 expr = ParseElementAccess(expr); 856 } 857 else { 858 break; 859 } 860 } 861 return expr; 862 } 863 864 Expression ParsePrimaryStart() { 865 switch (token.id) { 866 case TokenId.Identifier: 867 return ParseIdentifier(); 868 case TokenId.StringLiteral: 869 return ParseStringLiteral(); 870 case TokenId.IntegerLiteral: 871 return ParseIntegerLiteral(); 872 case TokenId.RealLiteral: 873 return ParseRealLiteral(); 874 case TokenId.OpenParen: 875 return ParseParenExpression(); 876 default: 877 throw ParseError(Res.ExpressionExpected); 878 } 879 } 880 881 Expression ParseStringLiteral() { 882 ValidateToken(TokenId.StringLiteral); 883 char quote = token.text[0]; 884 string s = token.text.Substring(1, token.text.Length - 2); 885 int start = 0; 886 while (true) { 887 int i = s.IndexOf(quote, start); 888 if (i < 0) break; 889 s = s.Remove(i, 1); 890 start = i + 1; 891 } 892 if (quote == '\'') { 893 if (s.Length != 1) 894 throw ParseError(Res.InvalidCharacterLiteral); 895 NextToken(); 896 return CreateLiteral(s[0], s); 897 } 898 NextToken(); 899 return CreateLiteral(s, s); 900 } 901 902 Expression ParseIntegerLiteral() { 903 ValidateToken(TokenId.IntegerLiteral); 904 string text = token.text; 905 if (text[0] != '-') { 906 ulong value; 907 if (!UInt64.TryParse(text, out value)) 908 throw ParseError(Res.InvalidIntegerLiteral, text); 909 NextToken(); 910 if (value <= (ulong)Int32.MaxValue) return CreateLiteral((int)value, text); 911 if (value <= (ulong)UInt32.MaxValue) return CreateLiteral((uint)value, text); 912 if (value <= (ulong)Int64.MaxValue) return CreateLiteral((long)value, text); 913 return CreateLiteral(value, text); 914 } 915 else { 916 long value; 917 if (!Int64.TryParse(text, out value)) 918 throw ParseError(Res.InvalidIntegerLiteral, text); 919 NextToken(); 920 if (value >= Int32.MinValue && value <= Int32.MaxValue) 921 return CreateLiteral((int)value, text); 922 return CreateLiteral(value, text); 923 } 924 } 925 926 Expression ParseRealLiteral() { 927 ValidateToken(TokenId.RealLiteral); 928 string text = token.text; 929 object value = null; 930 char last = text[text.Length - 1]; 931 if (last == 'F' || last == 'f') { 932 float f; 933 if (Single.TryParse(text.Substring(0, text.Length - 1), out f)) value = f; 934 } 935 else { 936 double d; 937 if (Double.TryParse(text, out d)) value = d; 938 } 939 if (value == null) throw ParseError(Res.InvalidRealLiteral, text); 940 NextToken(); 941 return CreateLiteral(value, text); 942 } 943 944 Expression CreateLiteral(object value, string text) { 945 ConstantExpression expr = Expression.Constant(value); 946 literals.Add(expr, text); 947 return expr; 948 } 949 950 Expression ParseParenExpression() { 951 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected); 952 NextToken(); 953 Expression e = ParseExpression(); 954 ValidateToken(TokenId.CloseParen, Res.CloseParenOrOperatorExpected); 955 NextToken(); 956 return e; 957 } 958 959 Expression ParseIdentifier() { 960 ValidateToken(TokenId.Identifier); 961 object value; 962 if (keywords.TryGetValue(token.text, out value)) { 963 if (value is Type) return ParseTypeAccess((Type)value); 964 if (value == (object)keywordIt) return ParseIt(); 965 if (value == (object)keywordIif) return ParseIif(); 966 if (value == (object)keywordNew) return ParseNew(); 967 NextToken(); 968 return (Expression)value; 969 } 970 if (symbols.TryGetValue(token.text, out value) || 971 externals != null && externals.TryGetValue(token.text, out value)) { 972 Expression expr = value as Expression; 973 if (expr == null) { 974 expr = Expression.Constant(value); 975 } 976 else { 977 LambdaExpression lambda = expr as LambdaExpression; 978 if (lambda != null) return ParseLambdaInvocation(lambda); 979 } 980 NextToken(); 981 return expr; 982 } 983 if (it != null) return ParseMemberAccess(null, it); 984 throw ParseError(Res.UnknownIdentifier, token.text); 985 } 986 987 Expression ParseIt() { 988 if (it == null) 989 throw ParseError(Res.NoItInScope); 990 NextToken(); 991 return it; 992 } 993 994 Expression ParseIif() { 995 int errorPos = token.pos; 996 NextToken(); 997 Expression[] args = ParseArgumentList(); 998 if (args.Length != 3) 999 throw ParseError(errorPos, Res.IifRequiresThreeArgs);1000 return GenerateConditional(args[0], args[1], args[2], errorPos);1001 }1002 1003 Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos) {1004 if (test.Type != typeof(bool))1005 throw ParseError(errorPos, Res.FirstExprMustBeBool);1006 if (expr1.Type != expr2.Type) {1007 Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;1008 Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;1009 if (expr1as2 != null && expr2as1 == null) {1010 expr1 = expr1as2;1011 }1012 else if (expr2as1 != null && expr1as2 == null) {1013 expr2 = expr2as1;1014 }1015 else {1016 string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null";1017 string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null";1018 if (expr1as2 != null && expr2as1 != null)1019 throw ParseError(errorPos, Res.BothTypesConvertToOther, type1, type2);1020 throw ParseError(errorPos, Res.NeitherTypeConvertsToOther, type1, type2);1021 }1022 }1023 return Expression.Condition(test, expr1, expr2);1024 }1025 1026 Expression ParseNew() {1027 NextToken();1028 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);1029 NextToken();1030 List<DynamicProperty> properties = new List<DynamicProperty>();1031 List<Expression> expressions = new List<Expression>();1032 while (true) {1033 int exprPos = token.pos;1034 Expression expr = ParseExpression();1035 string propName;1036 if (TokenIdentifierIs("as")) {1037 NextToken();1038 propName = GetIdentifier();1039 NextToken();1040 }1041 else {1042 MemberExpression me = expr as MemberExpression;1043 if (me == null) throw ParseError(exprPos, Res.MissingAsClause);1044 propName = me.Member.Name;1045 }1046 expressions.Add(expr);1047 properties.Add(new DynamicProperty(propName, expr.Type));1048 if (token.id != TokenId.Comma) break;1049 NextToken();1050 }1051 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);1052 NextToken();1053 Type type = DynamicExpression.CreateClass(properties);1054 MemberBinding[] bindings = new MemberBinding[properties.Count];1055 for (int i = 0; i < bindings.Length; i++)1056 bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);1057 return Expression.MemberInit(Expression.New(type), bindings);1058 }1059 1060 Expression ParseLambdaInvocation(LambdaExpression lambda) {1061 int errorPos = token.pos;1062 NextToken();1063 Expression[] args = ParseArgumentList();1064 MethodBase method;1065 if (FindMethod(lambda.Type, "Invoke", false, args, out method) != 1)1066 throw ParseError(errorPos, Res.ArgsIncompatibleWithLambda);1067 return Expression.Invoke(lambda, args);1068 }1069 1070 Expression ParseTypeAccess(Type type) {1071 int errorPos = token.pos;1072 NextToken();1073 if (token.id == TokenId.Question) {1074 if (!type.IsValueType || IsNullableType(type))1075 throw ParseError(errorPos, Res.TypeHasNoNullableForm, GetTypeName(type));1076 type = typeof(Nullable<>).MakeGenericType(type);1077 NextToken();1078 }1079 if (token.id == TokenId.OpenParen) {1080 Expression[] args = ParseArgumentList();1081 MethodBase method;1082 switch (FindBestMethod(type.GetConstructors(), args, out method)) {1083 case 0:1084 if (args.Length == 1)1085 return GenerateConversion(args[0], type, errorPos);1086 throw ParseError(errorPos, Res.NoMatchingConstructor, GetTypeName(type));1087 case 1:1088 return Expression.New((ConstructorInfo)method, args);1089 default:1090 throw ParseError(errorPos, Res.AmbiguousConstructorInvocation, GetTypeName(type));1091 }1092 }1093 ValidateToken(TokenId.Dot, Res.DotOrOpenParenExpected);1094 NextToken();1095 return ParseMemberAccess(type, null);1096 }1097 1098 Expression GenerateConversion(Expression expr, Type type, int errorPos) {1099 Type exprType = expr.Type;1100 if (exprType == type) return expr;1101 if (exprType.IsValueType && type.IsValueType) {1102 if ((IsNullableType(exprType) || IsNullableType(type)) &&1103 GetNonNullableType(exprType) == GetNonNullableType(type))1104 return Expression.Convert(expr, type);1105 if ((IsNumericType(exprType) || IsEnumType(exprType)) &&1106 (IsNumericType(type)) || IsEnumType(type))1107 return Expression.ConvertChecked(expr, type);1108 }1109 if (exprType.IsAssignableFrom(type) || type.IsAssignableFrom(exprType) ||1110 exprType.IsInterface || type.IsInterface)1111 return Expression.Convert(expr, type);1112 throw ParseError(errorPos, Res.CannotConvertValue,1113 GetTypeName(exprType), GetTypeName(type));1114 }1115 1116 Expression ParseMemberAccess(Type type, Expression instance) {1117 if (instance != null) type = instance.Type;1118 int errorPos = token.pos;1119 string id = GetIdentifier();1120 NextToken();1121 if (token.id == TokenId.OpenParen) {1122 if (instance != null && type != typeof(string)) {1123 Type enumerableType = FindGenericType(typeof(IEnumerable<>), type);1124 if (enumerableType != null) {1125 Type elementType = enumerableType.GetGenericArguments()[0];1126 return ParseAggregate(instance, elementType, id, errorPos);1127 }1128 }1129 Expression[] args = ParseArgumentList();1130 MethodBase mb;1131 switch (FindMethod(type, id, instance == null, args, out mb)) {1132 case 0:1133 throw ParseError(errorPos, Res.NoApplicableMethod,1134 id, GetTypeName(type));1135 case 1:1136 MethodInfo method = (MethodInfo)mb;1137 if (!IsPredefinedType(method.DeclaringType))1138 throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType));1139 if (method.ReturnType == typeof(void))1140 throw ParseError(errorPos, Res.MethodIsVoid,1141 id, GetTypeName(method.DeclaringType));1142 return Expression.Call(instance, (MethodInfo)method, args);1143 default:1144 throw ParseError(errorPos, Res.AmbiguousMethodInvocation,1145 id, GetTypeName(type));1146 }1147 }1148 else {1149 MemberInfo member = FindPropertyOrField(type, id, instance == null);1150 if (member == null)1151 throw ParseError(errorPos, Res.UnknownPropertyOrField,1152 id, GetTypeName(type));1153 return member is PropertyInfo ?1154 Expression.Property(instance, (PropertyInfo)member) :1155 Expression.Field(instance, (FieldInfo)member);1156 }1157 }1158 1159 static Type FindGenericType(Type generic, Type type) {1160 while (type != null && type != typeof(object)) {1161 if (type.IsGenericType && type.GetGenericTypeDefinition() == generic) return type;1162 if (generic.IsInterface) {1163 foreach (Type intfType in type.GetInterfaces()) {1164 Type found = FindGenericType(generic, intfType);1165 if (found != null) return found;1166 }1167 }1168 type = type.BaseType;1169 }1170 return null;1171 }1172 1173 Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos) {1174 ParameterExpression outerIt = it;1175 ParameterExpression innerIt = Expression.Parameter(elementType, "");1176 it = innerIt;1177 Expression[] args = ParseArgumentList();1178 it = outerIt;1179 MethodBase signature;1180 if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)1181 throw ParseError(errorPos, Res.NoApplicableAggregate, methodName);1182 Type[] typeArgs;1183 if (signature.Name == "Min" || signature.Name == "Max") {1184 typeArgs = new Type[] { elementType, args[0].Type };1185 }1186 else {1187 typeArgs = new Type[] { elementType };1188 }1189 if (args.Length == 0) {1190 args = new Expression[] { instance };1191 }1192 else {1193 args = new Expression[] { instance, Expression.Lambda(args[0], innerIt) };1194 }1195 return Expression.Call(typeof(Enumerable), signature.Name, typeArgs, args);1196 }1197 1198 Expression[] ParseArgumentList() {1199 ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);1200 NextToken();1201 Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : new Expression[0];1202 ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);1203 NextToken();1204 return args;1205 }1206 1207 Expression[] ParseArguments() {1208 List<Expression> argList = new List<Expression>();1209 while (true) {1210 argList.Add(ParseExpression());1211 if (token.id != TokenId.Comma) break;1212 NextToken();1213 }1214 return argList.ToArray();1215 }1216 1217 Expression ParseElementAccess(Expression expr) {1218 int errorPos = token.pos;1219 ValidateToken(TokenId.OpenBracket, Res.OpenParenExpected);1220 NextToken();1221 Expression[] args = ParseArguments();1222 ValidateToken(TokenId.CloseBracket, Res.CloseBracketOrCommaExpected);1223 NextToken();1224 if (expr.Type.IsArray) {1225 if (expr.Type.GetArrayRank() != 1 || args.Length != 1)1226 throw ParseError(errorPos, Res.CannotIndexMultiDimArray);1227 Expression index = PromoteExpression(args[0], typeof(int), true);1228 if (index == null)1229 throw ParseError(errorPos, Res.InvalidIndex);1230 return Expression.ArrayIndex(expr, index);1231 }1232 else {1233 MethodBase mb;1234 switch (FindIndexer(expr.Type, args, out mb)) {1235 case 0:1236 throw ParseError(errorPos, Res.NoApplicableIndexer,1237 GetTypeName(expr.Type));1238 case 1:1239 return Expression.Call(expr, (MethodInfo)mb, args);1240 default:1241 throw ParseError(errorPos, Res.AmbiguousIndexerInvocation,1242 GetTypeName(expr.Type));1243 }1244 }1245 }1246 1247 static bool IsPredefinedType(Type type) {1248 foreach (Type t in predefinedTypes) if (t == type) return true;1249 return false;1250 }1251 1252 static bool IsNullableType(Type type) {1253 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);1254 }1255 1256 static Type GetNonNullableType(Type type) {1257 return IsNullableType(type) ? type.GetGenericArguments()[0] : type;1258 }1259 1260 static string GetTypeName(Type type) {1261 Type baseType = GetNonNullableType(type);1262 string s = baseType.Name;1263 if (type != baseType) s += '?';1264 return s;1265 }1266 1267 static bool IsNumericType(Type type) {1268 return GetNumericTypeKind(type) != 0;1269 }1270 1271 static bool IsSignedIntegralType(Type type) {1272 return GetNumericTypeKind(type) == 2;1273 }1274 1275 static bool IsUnsignedIntegralType(Type type) {1276 return GetNumericTypeKind(type) == 3;1277 }1278 1279 static int GetNumericTypeKind(Type type) {1280 type = GetNonNullableType(type);1281 if (type.IsEnum) return 0;1282 switch (Type.GetTypeCode(type)) {1283 case TypeCode.Char:1284 case TypeCode.Single:1285 case TypeCode.Double:1286 case TypeCode.Decimal:1287 return 1;1288 case TypeCode.SByte:1289 case TypeCode.Int16:1290 case TypeCode.Int32:1291 case TypeCode.Int64:1292 return 2;1293 case TypeCode.Byte:1294 case TypeCode.UInt16:1295 case TypeCode.UInt32:1296 case TypeCode.UInt64:1297 return 3;1298 default:1299 return 0;1300 }1301 }1302 1303 static bool IsEnumType(Type type) {1304 return GetNonNullableType(type).IsEnum;1305 }1306 1307 void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos) {1308 Expression[] args = new Expression[] { expr };1309 MethodBase method;1310 if (FindMethod(signatures, "F", false, args, out method) != 1)1311 throw ParseError(errorPos, Res.IncompatibleOperand,1312 opName, GetTypeName(args[0].Type));1313 expr = args[0];1314 }1315 1316 void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos) {1317 Expression[] args = new Expression[] { left, right };1318 MethodBase method;1319 if (FindMethod(signatures, "F", false, args, out method) != 1)1320 throw IncompatibleOperandsError(opName, left, right, errorPos);1321 left = args[0];1322 right = args[1];1323 }1324 1325 Exception IncompatibleOperandsError(string opName, Expression left, Expression right, int pos) {1326 return ParseError(pos, Res.IncompatibleOperands,1327 opName, GetTypeName(left.Type), GetTypeName(right.Type));1328 }1329 1330 MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess) {1331 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |1332 (staticAccess ? BindingFlags.Static : BindingFlags.Instance);1333 foreach (Type t in SelfAndBaseTypes(type)) {1334 MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field,1335 flags, Type.FilterNameIgnoreCase, memberName);1336 if (members.Length != 0) return members[0];1337 }1338 return null;1339 }1340 1341 int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method) {1342 BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |1343 (staticAccess ? BindingFlags.Static : BindingFlags.Instance);1344 foreach (Type t in SelfAndBaseTypes(type)) {1345 MemberInfo[] members = t.FindMembers(MemberTypes.Method,1346 flags, Type.FilterNameIgnoreCase, methodName);1347 int count = FindBestMethod(members.Cast<MethodBase>(), args, out method);1348 if (count != 0) return count;1349 }1350 method = null;1351 return 0;1352 }1353 1354 int FindIndexer(Type type, Expression[] args, out MethodBase method) {1355 foreach (Type t in SelfAndBaseTypes(type)) {1356 MemberInfo[] members = t.GetDefaultMembers();1357 if (members.Length != 0) {1358 IEnumerable<MethodBase> methods = members.1359 OfType<PropertyInfo>().1360 Select(p => (MethodBase)p.GetGetMethod()).1361 Where(m => m != null);1362 int count = FindBestMethod(methods, args, out method);1363 if (count != 0) return count;1364 }1365 }1366 method = null;1367 return 0;1368 }1369 1370 static IEnumerable<Type> SelfAndBaseTypes(Type type) {1371 if (type.IsInterface) {1372 List<Type> types = new List<Type>();1373 AddInterface(types, type);1374 return types;1375 }1376 return SelfAndBaseClasses(type);1377 }1378 1379 static IEnumerable<Type> SelfAndBaseClasses(Type type) {1380 while (type != null) {1381 yield return type;1382 type = type.BaseType;1383 }1384 }1385 1386 static void AddInterface(List<Type> types, Type type) {1387 if (!types.Contains(type)) {1388 types.Add(type);1389 foreach (Type t in type.GetInterfaces()) AddInterface(types, t);1390 }1391 }1392 1393 class MethodData1394 {1395 public MethodBase MethodBase;1396 public ParameterInfo[] Parameters;1397 public Expression[] Args;1398 }1399 1400 int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method) {1401 MethodData[] applicable = methods.1402 Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }).1403 Where(m => IsApplicable(m, args)).1404 ToArray();1405 if (applicable.Length > 1) {1406 applicable = applicable.1407 Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).1408 ToArray();1409 }1410 if (applicable.Length == 1) {1411 MethodData md = applicable[0];1412 for (int i = 0; i < args.Length; i++) args[i] = md.Args[i];1413 method = md.MethodBase;1414 }1415 else {1416 method = null;1417 }1418 return applicable.Length;1419 }1420 1421 bool IsApplicable(MethodData method, Expression[] args) {1422 if (method.Parameters.Length != args.Length) return false;1423 Expression[] promotedArgs = new Expression[args.Length];1424 for (int i = 0; i < args.Length; i++) {1425 ParameterInfo pi = method.Parameters[i];1426 if (pi.IsOut) return false;1427 Expression promoted = PromoteExpression(args[i], pi.ParameterType, false);1428 if (promoted == null) return false;1429 promotedArgs[i] = promoted;1430 }1431 method.Args = promotedArgs;1432 return true;1433 }1434 1435 Expression PromoteExpression(Expression expr, Type type, bool exact) {1436 if (expr.Type == type) return expr;1437 if (expr is ConstantExpression) {1438 ConstantExpression ce = (ConstantExpression)expr;1439 if (ce == nullLiteral) {1440 if (!type.IsValueType || IsNullableType(type))1441 return Expression.Constant(null, type);1442 }1443 else {1444 string text;1445 if (literals.TryGetValue(ce, out text)) {1446 Type target = GetNonNullableType(type);1447 Object value = null;1448 switch (Type.GetTypeCode(ce.Type)) {1449 case TypeCode.Int32:1450 case TypeCode.UInt32:1451 case TypeCode.Int64:1452 case TypeCode.UInt64:1453 value = ParseNumber(text, target);1454 break;1455 case TypeCode.Double:1456 if (target == typeof(decimal)) value = ParseNumber(text, target);1457 break;1458 case TypeCode.String:1459 value = ParseEnum(text, target);1460 break;1461 }1462 if (value != null)1463 return Expression.Constant(value, type);1464 }1465 }1466 }1467 if (IsCompatibleWith(expr.Type, type)) {1468 if (type.IsValueType || exact) return Expression.Convert(expr, type);1469 return expr;1470 }1471 return null;1472 }1473 1474 static object ParseNumber(string text, Type type) {1475 switch (Type.GetTypeCode(GetNonNullableType(type))) {1476 case TypeCode.SByte:1477 sbyte sb;1478 if (sbyte.TryParse(text, out sb)) return sb;1479 break;1480 case TypeCode.Byte:1481 byte b;1482 if (byte.TryParse(text, out b)) return b;1483 break;1484 case TypeCode.Int16:1485 short s;1486 if (short.TryParse(text, out s)) return s;1487 break;1488 case TypeCode.UInt16:1489 ushort us;1490 if (ushort.TryParse(text, out us)) return us;1491 break;1492 case TypeCode.Int32:1493 int i;1494 if (int.TryParse(text, out i)) return i;1495 break;1496 case TypeCode.UInt32:1497 uint ui;1498 if (uint.TryParse(text, out ui)) return ui;1499 break;1500 case TypeCode.Int64:1501 long l;1502 if (long.TryParse(text, out l)) return l;1503 break;1504 case TypeCode.UInt64:1505 ulong ul;1506 if (ulong.TryParse(text, out ul)) return ul;1507 break;1508 case TypeCode.Single:1509 float f;1510 if (float.TryParse(text, out f)) return f;1511 break;1512 case TypeCode.Double:1513 double d;1514 if (double.TryParse(text, out d)) return d;1515 break;1516 case TypeCode.Decimal:1517 decimal e;1518 if (decimal.TryParse(text, out e)) return e;1519 break;1520 }1521 return null;1522 }1523 1524 static object ParseEnum(string name, Type type) {1525 if (type.IsEnum) {1526 MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field,1527 BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static,1528 Type.FilterNameIgnoreCase, name);1529 if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null);1530 }1531 return null;1532 }1533 1534 static bool IsCompatibleWith(Type source, Type target) {1535 if (source == target) return true;1536 if (!target.IsValueType) return target.IsAssignableFrom(source);1537 Type st = GetNonNullableType(source);1538 Type tt = GetNonNullableType(target);1539 if (st != source && tt == target) return false;1540 TypeCode sc = st.IsEnum ? TypeCode.Object : Type.GetTypeCode(st);1541 TypeCode tc = tt.IsEnum ? TypeCode.Object : Type.GetTypeCode(tt);1542 switch (sc) {1543 case TypeCode.SByte:1544 switch (tc) {1545 case TypeCode.SByte:1546 case TypeCode.Int16:1547 case TypeCode.Int32:1548 case TypeCode.Int64:1549 case TypeCode.Single:1550 case TypeCode.Double:1551 case TypeCode.Decimal:1552 return true;1553 }1554 break;1555 case TypeCode.Byte:1556 switch (tc) {1557 case TypeCode.Byte:1558 case TypeCode.Int16:1559 case TypeCode.UInt16:1560 case TypeCode.Int32:1561 case TypeCode.UInt32:1562 case TypeCode.Int64:1563 case TypeCode.UInt64:1564 case TypeCode.Single:1565 case TypeCode.Double:1566 case TypeCode.Decimal:1567 return true;1568 }1569 break;1570 case TypeCode.Int16:1571 switch (tc) {1572 case TypeCode.Int16:1573 case TypeCode.Int32:1574 case TypeCode.Int64:1575 case TypeCode.Single:1576 case TypeCode.Double:1577 case TypeCode.Decimal:1578 return true;1579 }1580 break;1581 case TypeCode.UInt16:1582 switch (tc) {1583 case TypeCode.UInt16:1584 case TypeCode.Int32:1585 case TypeCode.UInt32:1586 case TypeCode.Int64:1587 case TypeCode.UInt64:1588 case TypeCode.Single:1589 case TypeCode.Double:1590 case TypeCode.Decimal:1591 return true;1592 }1593 break;1594 case TypeCode.Int32:1595 switch (tc) {1596 case TypeCode.Int32:1597 case TypeCode.Int64:1598 case TypeCode.Single:1599 case TypeCode.Double:1600 case TypeCode.Decimal:1601 return true;1602 }1603 break;1604 case TypeCode.UInt32:1605 switch (tc) {1606 case TypeCode.UInt32:1607 case TypeCode.Int64:1608 case TypeCode.UInt64:1609 case TypeCode.Single:1610 case TypeCode.Double:1611 case TypeCode.Decimal:1612 return true;1613 }1614 break;1615 case TypeCode.Int64:1616 switch (tc) {1617 case TypeCode.Int64:1618 case TypeCode.Single:1619 case TypeCode.Double:1620 case TypeCode.Decimal:1621 return true;1622 }1623 break;1624 case TypeCode.UInt64:1625 switch (tc) {1626 case TypeCode.UInt64:1627 case TypeCode.Single:1628 case TypeCode.Double:1629 case TypeCode.Decimal:1630 return true;1631 }1632 break;1633 case TypeCode.Single:1634 switch (tc) {1635 case TypeCode.Single:1636 case TypeCode.Double:1637 return true;1638 }1639 break;1640 default:1641 if (st == tt) return true;1642 break;1643 }1644 return false;1645 }1646 1647 static bool IsBetterThan(Expression[] args, MethodData m1, MethodData m2) {1648 bool better = false;1649 for (int i = 0; i < args.Length; i++) {1650 int c = CompareConversions(args[i].Type,1651 m1.Parameters[i].ParameterType,1652 m2.Parameters[i].ParameterType);1653 if (c < 0) return false;1654 if (c > 0) better = true;1655 }1656 return better;1657 }1658 1659 // Return 1 if s -> t1 is a better conversion than s -> t21660 // Return -1 if s -> t2 is a better conversion than s -> t11661 // Return 0 if neither conversion is better1662 static int CompareConversions(Type s, Type t1, Type t2) {1663 if (t1 == t2) return 0;1664 if (s == t1) return 1;1665 if (s == t2) return -1;1666 bool t1t2 = IsCompatibleWith(t1, t2);1667 bool t2t1 = IsCompatibleWith(t2, t1);1668 if (t1t2 && !t2t1) return 1;1669 if (t2t1 && !t1t2) return -1;1670 if (IsSignedIntegralType(t1) && IsUnsignedIntegralType(t2)) return 1;1671 if (IsSignedIntegralType(t2) && IsUnsignedIntegralType(t1)) return -1;1672 return 0;1673 }1674 1675 Expression GenerateEqual(Expression left, Expression right) {1676 return Expression.Equal(left, right);1677 }1678 1679 Expression GenerateNotEqual(Expression left, Expression right) {1680 return Expression.NotEqual(left, right);1681 }1682 1683 Expression GenerateGreaterThan(Expression left, Expression right) {1684 if (left.Type == typeof(string)) {1685 return Expression.GreaterThan(1686 GenerateStaticMethodCall("Compare", left, right),1687 Expression.Constant(0)1688 );1689 }1690 return Expression.GreaterThan(left, right);1691 }1692 1693 Expression GenerateGreaterThanEqual(Expression left, Expression right) {1694 if (left.Type == typeof(string)) {1695 return Expression.GreaterThanOrEqual(1696 GenerateStaticMethodCall("Compare", left, right),1697 Expression.Constant(0)1698 );1699 }1700 return Expression.GreaterThanOrEqual(left, right);1701 }1702 1703 Expression GenerateLessThan(Expression left, Expression right) {1704 if (left.Type == typeof(string)) {1705 return Expression.LessThan(1706 GenerateStaticMethodCall("Compare", left, right),1707 Expression.Constant(0)1708 );1709 }1710 return Expression.LessThan(left, right);1711 }1712 1713 Expression GenerateLessThanEqual(Expression left, Expression right) {1714 if (left.Type == typeof(string)) {1715 return Expression.LessThanOrEqual(1716 GenerateStaticMethodCall("Compare", left, right),1717 Expression.Constant(0)1718 );1719 }1720 return Expression.LessThanOrEqual(left, right);1721 }1722 1723 Expression GenerateAdd(Expression left, Expression right) {1724 if (left.Type == typeof(string) && right.Type == typeof(string)) {1725 return GenerateStaticMethodCall("Concat", left, right);1726 }1727 return Expression.Add(left, right);1728 }1729 1730 Expression GenerateSubtract(Expression left, Expression right) {1731 return Expression.Subtract(left, right);1732 }1733 1734 Expression GenerateStringConcat(Expression left, Expression right) {1735 return Expression.Call(1736 null,1737 typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),1738 new[] { left, right });1739 }1740 1741 MethodInfo GetStaticMethod(string methodName, Expression left, Expression right) {1742 return left.Type.GetMethod(methodName, new[] { left.Type, right.Type });1743 }1744 1745 Expression GenerateStaticMethodCall(string methodName, Expression left, Expression right) {1746 return Expression.Call(null, GetStaticMethod(methodName, left, right), new[] { left, right });1747 }1748 1749 void SetTextPos(int pos) {1750 textPos = pos;1751 ch = textPos < textLen ? text[textPos] : '\0';1752 }1753 1754 void NextChar() {1755 if (textPos < textLen) textPos++;1756 ch = textPos < textLen ? text[textPos] : '\0';1757 }1758 1759 void NextToken() {1760 while (Char.IsWhiteSpace(ch)) NextChar();1761 TokenId t;1762 int tokenPos = textPos;1763 switch (ch) {1764 case '!':1765 NextChar();1766 if (ch == '=') {1767 NextChar();1768 t = TokenId.ExclamationEqual;1769 }1770 else {1771 t = TokenId.Exclamation;1772 }1773 break;1774 case '%':1775 NextChar();1776 t = TokenId.Percent;1777 break;1778 case '&':1779 NextChar();1780 if (ch == '&') {1781 NextChar();1782 t = TokenId.DoubleAmphersand;1783 }1784 else {1785 t = TokenId.Amphersand;1786 }1787 break;1788 case '(':1789 NextChar();1790 t = TokenId.OpenParen;1791 break;1792 case ')':1793 NextChar();1794 t = TokenId.CloseParen;1795 break;1796 case '*':1797 NextChar();1798 t = TokenId.Asterisk;1799 break;1800 case '+':1801 NextChar();1802 t = TokenId.Plus;1803 break;1804 case ',':1805 NextChar();1806 t = TokenId.Comma;1807 break;1808 case '-':1809 NextChar();1810 t = TokenId.Minus;1811 break;1812 case '.':1813 NextChar();1814 t = TokenId.Dot;1815 break;1816 case '/':1817 NextChar();1818 t = TokenId.Slash;1819 break;1820 case ':':1821 NextChar();1822 t = TokenId.Colon;1823 break;1824 case '<':1825 NextChar();1826 if (ch == '=') {1827 NextChar();1828 t = TokenId.LessThanEqual;1829 }1830 else if (ch == '>') {1831 NextChar();1832 t = TokenId.LessGreater;1833 }1834 else {1835 t = TokenId.LessThan;1836 }1837 break;1838 case '=':1839 NextChar();1840 if (ch == '=') {1841 NextChar();1842 t = TokenId.DoubleEqual;1843 }1844 else {1845 t = TokenId.Equal;1846 }1847 break;1848 case '>':1849 NextChar();1850 if (ch == '=') {1851 NextChar();1852 t = TokenId.GreaterThanEqual;1853 }1854 else {1855 t = TokenId.GreaterThan;1856 }1857 break;1858 case '?':1859 NextChar();1860 t = TokenId.Question;1861 break;1862 case '[':1863 NextChar();1864 t = TokenId.OpenBracket;1865 break;1866 case ']':1867 NextChar();1868 t = TokenId.CloseBracket;1869 break;1870 case '|':1871 NextChar();1872 if (ch == '|') {1873 NextChar();1874 t = TokenId.DoubleBar;1875 }1876 else {1877 t = TokenId.Bar;1878 }1879 break;1880 case '"':1881 case '\'':1882 char quote = ch;1883 do {1884 NextChar();1885 while (textPos < textLen && ch != quote) NextChar();1886 if (textPos == textLen)1887 throw ParseError(textPos, Res.UnterminatedStringLiteral);1888 NextChar();1889 } while (ch == quote);1890 t = TokenId.StringLiteral;1891 break;1892 default:1893 if (Char.IsLetter(ch) || ch == '@' || ch == '_') {1894 do {1895 NextChar();1896 } while (Char.IsLetterOrDigit(ch) || ch == '_');1897 t = TokenId.Identifier;1898 break;1899 }1900 if (Char.IsDigit(ch)) {1901 t = TokenId.IntegerLiteral;1902 do {1903 NextChar();1904 } while (Char.IsDigit(ch));1905 if (ch == '.') {1906 t = TokenId.RealLiteral;1907 NextChar();1908 ValidateDigit();1909 do {1910 NextChar();1911 } while (Char.IsDigit(ch));1912 }1913 if (ch == 'E' || ch == 'e') {1914 t = TokenId.RealLiteral;1915 NextChar();1916 if (ch == '+' || ch == '-') NextChar();1917 ValidateDigit();1918 do {1919 NextChar();1920 } while (Char.IsDigit(ch));1921 }1922 if (ch == 'F' || ch == 'f') NextChar();1923 break;1924 }1925 if (textPos == textLen) {1926 t = TokenId.End;1927 break;1928 }1929 throw ParseError(textPos, Res.InvalidCharacter, ch);1930 }1931 token.id = t;1932 token.text = text.Substring(tokenPos, textPos - tokenPos);1933 token.pos = tokenPos;1934 }1935 1936 bool TokenIdentifierIs(string id) {1937 return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase);1938 }1939 1940 string GetIdentifier() {1941 ValidateToken(TokenId.Identifier, Res.IdentifierExpected);1942 string id = token.text;1943 if (id.Length > 1 && id[0] == '@') id = id.Substring(1);1944 return id;1945 }1946 1947 void ValidateDigit() {1948 if (!Char.IsDigit(ch)) throw ParseError(textPos, Res.DigitExpected);1949 }1950 1951 void ValidateToken(TokenId t, string errorMessage) {1952 if (token.id != t) throw ParseError(errorMessage);1953 }1954 1955 void ValidateToken(TokenId t) {1956 if (token.id != t) throw ParseError(Res.SyntaxError);1957 }1958 1959 Exception ParseError(string format, params object[] args) {1960 return ParseError(token.pos, format, args);1961 }1962 1963 Exception ParseError(int pos, string format, params object[] args) {1964 return new ParseException(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args), pos);1965 }1966 1967 static Dictionary<string, object> CreateKeywords() {1968 Dictionary<string, object> d = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);1969 d.Add("true", trueLiteral);1970 d.Add("false", falseLiteral);1971 d.Add("null", nullLiteral);1972 d.Add(keywordIt, keywordIt);1973 d.Add(keywordIif, keywordIif);1974 d.Add(keywordNew, keywordNew);1975 foreach (Type type in predefinedTypes) d.Add(type.Name, type);1976 return d;1977 }1978 }1979 1980 static class Res1981 {1982 public const string DuplicateIdentifier = "The identifier '{0}' was defined more than once";1983 public const string ExpressionTypeMismatch = "Expression of type '{0}' expected";1984 public const string ExpressionExpected = "Expression expected";1985 public const string InvalidCharacterLiteral = "Character literal must contain exactly one character";1986 public const string InvalidIntegerLiteral = "Invalid integer literal '{0}'";1987 public const string InvalidRealLiteral = "Invalid real literal '{0}'";1988 public const string UnknownIdentifier = "Unknown identifier '{0}'";1989 public const string NoItInScope = "No 'it' is in scope";1990 public const string IifRequiresThreeArgs = "The 'iif' function requires three arguments";1991 public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'";1992 public const string BothTypesConvertToOther = "Both of the types '{0}' and '{1}' convert to the other";1993 public const string NeitherTypeConvertsToOther = "Neither of the types '{0}' and '{1}' converts to the other";1994 public const string MissingAsClause = "Expression is missing an 'as' clause";1995 public const string ArgsIncompatibleWithLambda = "Argument list incompatible with lambda expression";1996 public const string TypeHasNoNullableForm = "Type '{0}' has no nullable form";1997 public const string NoMatchingConstructor = "No matching constructor in type '{0}'";1998 public const string AmbiguousConstructorInvocation = "Ambiguous invocation of '{0}' constructor";1999 public const string CannotConvertValue = "A value of type '{0}' cannot be converted to type '{1}'";2000 public const string NoApplicableMethod = "No applicable method '{0}' exists in type '{1}'";2001 public const string MethodsAreInaccessible = "Methods on type '{0}' are not accessible";2002 public const string MethodIsVoid = "Method '{0}' in type '{1}' does not return a value";2003 public const string AmbiguousMethodInvocation = "Ambiguous invocation of method '{0}' in type '{1}'";2004 public const string UnknownPropertyOrField = "No property or field '{0}' exists in type '{1}'";2005 public const string NoApplicableAggregate = "No applicable aggregate method '{0}' exists";2006 public const string CannotIndexMultiDimArray = "Indexing of multi-dimensional arrays is not supported";2007 public const string InvalidIndex = "Array index must be an integer expression";2008 public const string NoApplicableIndexer = "No applicable indexer exists in type '{0}'";2009 public const string AmbiguousIndexerInvocation = "Ambiguous invocation of indexer in type '{0}'";2010 public const string IncompatibleOperand = "Operator '{0}' incompatible with operand type '{1}'";2011 public const string IncompatibleOperands = "Operator '{0}' incompatible with operand types '{1}' and '{2}'";2012 public const string UnterminatedStringLiteral = "Unterminated string literal";2013 public const string InvalidCharacter = "Syntax error '{0}'";2014 public const string DigitExpected = "Digit expected";2015 public const string SyntaxError = "Syntax error";2016 public const string TokenExpected = "{0} expected";2017 public const string ParseExceptionFormat = "{0} (at index {1})";2018 public const string ColonExpected = "':' expected";2019 public const string OpenParenExpected = "'(' expected";2020 public const string CloseParenOrOperatorExpected = "')' or operator expected";2021 public const string CloseParenOrCommaExpected = "')' or ',' expected";2022 public const string DotOrOpenParenExpected = "'.' or '(' expected";2023 public const string OpenBracketExpected = "'[' expected";2024 public const string CloseBracketOrCommaExpected = "']' or ',' expected";2025 public const string IdentifierExpected = "Identifier expected";2026 }2027 }
4.~/Controllers/下更新控制器StoreManagerController
1 public ActionResult Index(string sortOrder) 2 { 3 //return View(); 4 //var albums = db.Albums.Include("Genre").Include("Artist"); 5 //return View(albums.ToList()); 6 7 #region ViewBag资源 8 ViewBag.CreateLink = Resource.CreateLink; 9 ViewBag.EditLink = Resource.EditLink;10 ViewBag.DetailsLink = Resource.DetailsLink;11 ViewBag.DeleteLink = Resource.DeleteLink;12 ViewBag.GenreDisplay = Resource.GenreDisplay;13 ViewBag.ArtistDisplay = Resource.ArtistDisplay;14 ViewBag.TitleDisplay = Resource.TitleDisplay;15 ViewBag.PriceDisplay = Resource.PriceDisplay;16 #endregion17 18 #region ViewBag排序PARAMS19 ViewBag.GenreSortParam = (sortOrder == "Genre.Name") ? "Genre.Name desc" : "Genre.Name";20 ViewBag.ArtistSortParam = (sortOrder == "Artist.Name") ? "Artist.Name desc" : "Artist.Name";21 ViewBag.TitleSortParam = (sortOrder == "Title") ? "Title desc" : "Title";22 ViewBag.PriceSortParam = (sortOrder == "Price") ? "Price desc" : "Price";23 #endregion24 25 //默认的排序顺序26 if (String.IsNullOrEmpty(sortOrder))27 {28 sortOrder = "Title desc";29 }30 31 var albums = db.Albums.OrderBy(sortOrder);32 return View(albums.ToList());33 }
TAG: