| | | 1 | | // ----------------------------------------------------------------------- |
| | | 2 | | // <copyright file="ExpressionExtensions.cs" company="Stéphane ANDRE"> |
| | | 3 | | // Copyright (c) Stéphane ANDRE. All rights reserved. |
| | | 4 | | // </copyright> |
| | | 5 | | // ----------------------------------------------------------------------- |
| | | 6 | | |
| | | 7 | | using System; |
| | | 8 | | using System.Collections.Generic; |
| | | 9 | | using System.Linq.Expressions; |
| | | 10 | | using System.Reflection; |
| | | 11 | | |
| | | 12 | | #pragma warning disable IDE0130 // Namespace does not match folder structure |
| | | 13 | | namespace MyNet.Primitives; |
| | | 14 | | #pragma warning restore IDE0130 // Namespace does not match folder structure |
| | | 15 | | |
| | | 16 | | /// <summary> |
| | | 17 | | /// Provides extension methods for working with expressions, particularly for extracting property paths from sorting exp |
| | | 18 | | /// </summary> |
| | | 19 | | public static class ExpressionExtensions |
| | | 20 | | { |
| | | 21 | | extension(Expression? expression) |
| | | 22 | | { |
| | | 23 | | /// <summary> |
| | | 24 | | /// Determines whether two expressions are equal by comparing their string representations. |
| | | 25 | | /// </summary> |
| | | 26 | | /// <param name="b">The second expression to compare.</param> |
| | | 27 | | /// <returns>True if the expressions are similar; otherwise, false.</returns> |
| | 6 | 28 | | public bool IsSimilar(Expression? b) => expression?.ToString() == b?.ToString(); |
| | | 29 | | } |
| | | 30 | | |
| | | 31 | | extension<T>(Expression<T> expression) |
| | | 32 | | { |
| | | 33 | | /// <summary> |
| | | 34 | | /// Extracts the property path from a sorting expression. The expression should be a simple member access (e.g., |
| | | 35 | | /// </summary> |
| | | 36 | | /// <returns>The property path as a string.</returns> |
| | | 37 | | /// <exception cref="InvalidOperationException">Thrown when the expression is not a valid member access.</except |
| | | 38 | | public string PropertyName() |
| | | 39 | | { |
| | 54 | 40 | | var body = expression.Body; |
| | | 41 | | |
| | 54 | 42 | | if (body is UnaryExpression { NodeType: ExpressionType.Convert } unary) |
| | 0 | 43 | | body = unary.Operand; |
| | | 44 | | |
| | 54 | 45 | | if (body is not MemberExpression memberExpression) |
| | 3 | 46 | | throw new InvalidOperationException("Unable to infer sorting key from expression. Use a simple member ex |
| | | 47 | | |
| | 51 | 48 | | var path = new Stack<string>(); |
| | 51 | 49 | | Expression? current = memberExpression; |
| | | 50 | | |
| | 105 | 51 | | while (current is MemberExpression currentMember) |
| | | 52 | | { |
| | 54 | 53 | | path.Push(currentMember.Member.Name); |
| | 54 | 54 | | current = currentMember.Expression; |
| | 54 | 55 | | } |
| | | 56 | | |
| | 51 | 57 | | return string.Join('.', path); |
| | | 58 | | } |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Gets the stack of MemberInfo objects representing the member access path in the given expression, which can |
| | | 62 | | /// </summary> |
| | | 63 | | /// <returns>The stack of MemberInfo objects representing the member access path, or an empty stack if the expre |
| | | 64 | | public Stack<MemberInfo> GetMembers() |
| | | 65 | | { |
| | 3 | 66 | | var stack = new Stack<MemberInfo>(); |
| | 3 | 67 | | var current = expression.Body; |
| | | 68 | | |
| | 9 | 69 | | while (current is not null) |
| | | 70 | | { |
| | 9 | 71 | | if (current is MemberExpression member) |
| | | 72 | | { |
| | 6 | 73 | | stack.Push(member.Member); |
| | 6 | 74 | | current = member.Expression; |
| | | 75 | | } |
| | 3 | 76 | | else if (current is UnaryExpression unary) |
| | | 77 | | { |
| | 0 | 78 | | current = unary.Operand; |
| | | 79 | | } |
| | | 80 | | else |
| | | 81 | | { |
| | | 82 | | break; |
| | | 83 | | } |
| | | 84 | | } |
| | | 85 | | |
| | 3 | 86 | | return stack; |
| | | 87 | | } |
| | | 88 | | } |
| | | 89 | | |
| | | 90 | | extension<T, TProperty>(Expression<Func<T, TProperty>> expression) |
| | | 91 | | { |
| | | 92 | | /// <summary> |
| | | 93 | | /// Gets PropertyInfo from a given expression that represents a property access. The expression should be a simp |
| | | 94 | | /// </summary> |
| | | 95 | | /// <returns>The PropertyInfo representing the property accessed by the expression.</returns> |
| | | 96 | | /// <exception cref="ArgumentException">Thrown when the expression is not a valid member access or the member is |
| | 33 | 97 | | public PropertyInfo PropertyInfo => expression.Body is not MemberExpression member |
| | 33 | 98 | | ? throw new InvalidOperationException("Expression must be a member access.") |
| | 33 | 99 | | : member.Member as PropertyInfo ?? throw new InvalidOperationException( |
| | 33 | 100 | | "Member is not a property."); |
| | | 101 | | } |
| | | 102 | | } |
| | | 103 | | |