|
| 1 | +using System.Linq.Dynamic.Core; |
| 2 | +using System.Reflection; |
| 3 | +using System.Text; |
| 4 | +using System.Text.Json.Serialization; |
| 5 | + |
| 6 | +public static class QueryableExtension |
| 7 | +{ |
| 8 | + public static Dictionary<string, string> _filterOperations => new Dictionary<string, string> |
| 9 | + { |
| 10 | + {"eq", "=="}, |
| 11 | + {"ne", "!="}, |
| 12 | + {"contains", "Contains"}, |
| 13 | + }; |
| 14 | + |
| 15 | + public static (IQueryable, int?) Apply<T>(this IQueryable<T> queryable, Query query, int? maxTop = null, ISearchBinder<T>? searchBinder = null) |
| 16 | + { |
| 17 | + var result = (IQueryable)queryable; |
| 18 | + |
| 19 | + if (maxTop is not null && query.Top > maxTop) |
| 20 | + { |
| 21 | + throw new GoatQueryException("The value supplied for the query parameter 'Top' was greater than the maximum top allowed for this resource"); |
| 22 | + } |
| 23 | + |
| 24 | + // Filter |
| 25 | + if (!string.IsNullOrEmpty(query.Filter)) |
| 26 | + { |
| 27 | + var filters = StringHelper.SplitString(query.Filter); |
| 28 | + |
| 29 | + var where = new StringBuilder(); |
| 30 | + |
| 31 | + for (int i = 0; i < filters.Count; i++) |
| 32 | + { |
| 33 | + var filter = filters[i]; |
| 34 | + var opts = StringHelper.SplitStringByWhitespace(filter.Trim()); |
| 35 | + |
| 36 | + if (opts.Count != 3) |
| 37 | + { |
| 38 | + continue; |
| 39 | + } |
| 40 | + |
| 41 | + if (i > 0) |
| 42 | + { |
| 43 | + var prev = filters[i - 1]; |
| 44 | + where.Append($" {prev.Trim()} "); |
| 45 | + } |
| 46 | + |
| 47 | + var property = opts[0].Replace("\'", string.Empty); |
| 48 | + var operand = opts[1]; |
| 49 | + var value = opts[2].Replace("'", "\""); |
| 50 | + |
| 51 | + string? propertyName = typeof(T).GetProperties().FirstOrDefault(x => x.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name == property)?.Name; |
| 52 | + |
| 53 | + if (!string.IsNullOrEmpty(propertyName)) |
| 54 | + { |
| 55 | + property = propertyName; |
| 56 | + } |
| 57 | + |
| 58 | + if (typeof(T).GetProperties().FirstOrDefault(x => x.Name.Equals(property, StringComparison.OrdinalIgnoreCase))?.PropertyType == typeof(string)) |
| 59 | + { |
| 60 | + if (operand.Equals("contains", StringComparison.OrdinalIgnoreCase)) |
| 61 | + { |
| 62 | + where.Append($"{property}.ToLower().Contains({value}.ToLower())"); |
| 63 | + } |
| 64 | + else |
| 65 | + { |
| 66 | + where.Append($"{property}.ToLower() {_filterOperations[operand]} {value}.ToLower()"); |
| 67 | + } |
| 68 | + } |
| 69 | + else if (operand.Equals("contains", StringComparison.OrdinalIgnoreCase)) |
| 70 | + { |
| 71 | + where.Append($"{property}.{_filterOperations[operand]}({value})"); |
| 72 | + } |
| 73 | + else if (typeof(T).GetProperties().FirstOrDefault(x => x.Name.Equals(property, StringComparison.OrdinalIgnoreCase))?.PropertyType == typeof(Guid)) |
| 74 | + { |
| 75 | + where.Append($"{property} {_filterOperations[operand]} Guid({value})"); |
| 76 | + } |
| 77 | + else |
| 78 | + { |
| 79 | + where.Append($"{property} {_filterOperations[operand]} {value}"); |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + result = result.Where(where.ToString()); |
| 84 | + } |
| 85 | + |
| 86 | + // Search |
| 87 | + if (searchBinder is not null && !string.IsNullOrEmpty(query.Search)) |
| 88 | + { |
| 89 | + var searchExpression = searchBinder.Bind(query.Search); |
| 90 | + |
| 91 | + if (searchExpression is null) |
| 92 | + { |
| 93 | + throw new GoatQueryException("search binder does not return valid expression that can be parsed to where clause"); |
| 94 | + } |
| 95 | + |
| 96 | + result = result.Where(searchExpression); |
| 97 | + } |
| 98 | + |
| 99 | + int? count = null; |
| 100 | + |
| 101 | + // Count |
| 102 | + if (query.Count ?? false) |
| 103 | + { |
| 104 | + count = result.Count(); |
| 105 | + } |
| 106 | + |
| 107 | + // Order by |
| 108 | + if (!string.IsNullOrEmpty(query.OrderBy)) |
| 109 | + { |
| 110 | + result = result.OrderBy(query.OrderBy); |
| 111 | + } |
| 112 | + |
| 113 | + // Select |
| 114 | + if (!string.IsNullOrEmpty(query.Select)) |
| 115 | + { |
| 116 | + result = result.Select($"new {{ {query.Select} }}"); |
| 117 | + } |
| 118 | + |
| 119 | + // Skip |
| 120 | + if (query.Skip > 0) |
| 121 | + { |
| 122 | + result = result.Skip(query.Skip ?? 0); |
| 123 | + } |
| 124 | + |
| 125 | + // Top |
| 126 | + if (query.Top > 0) |
| 127 | + { |
| 128 | + result = result.Take(query.Top ?? 0); |
| 129 | + } |
| 130 | + |
| 131 | + return (result, count); |
| 132 | + } |
| 133 | +} |
0 commit comments