using Sieve.Models; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Sieve.Services { public class SievePropertyMapper { private readonly Dictionary>> _map = new Dictionary>>(); public PropertyFluentApi Property(Expression> expression) { if(!_map.ContainsKey(typeof(TEntity))) { _map.Add(typeof(TEntity), new List>()); } return new PropertyFluentApi(this, expression); } public class PropertyFluentApi { private readonly SievePropertyMapper _sievePropertyMapper; private readonly PropertyInfo _property; public PropertyFluentApi(SievePropertyMapper sievePropertyMapper, Expression> expression) { _sievePropertyMapper = sievePropertyMapper; (_fullName, _property) = GetPropertyInfo(expression); _name = _fullName; _canFilter = false; _canSort = false; } private string _name; private readonly string _fullName; private bool _canFilter; private bool _canSort; public PropertyFluentApi CanFilter() { _canFilter = true; UpdateMap(); return this; } public PropertyFluentApi CanSort() { _canSort = true; UpdateMap(); return this; } public PropertyFluentApi HasName(string name) { _name = name; UpdateMap(); return this; } private void UpdateMap() { var metadata = new SievePropertyMetadata() { Name = _name, FullName = _fullName, CanFilter = _canFilter, CanSort = _canSort }; var pair = new KeyValuePair(_property, metadata); _sievePropertyMapper._map[typeof(TEntity)].Add(pair); } private static (string, PropertyInfo) GetPropertyInfo(Expression> exp) { if (!(exp.Body is MemberExpression body)) { var ubody = (UnaryExpression)exp.Body; body = ubody.Operand as MemberExpression; } var member = body?.Member as PropertyInfo; var stack = new Stack(); while (body != null) { stack.Push(body.Member.Name); body = body.Expression as MemberExpression; } return (string.Join(".", stack.ToArray()), member); } } public (string, PropertyInfo) FindProperty( bool canSortRequired, bool canFilterRequired, string name, bool isCaseSensitive) { try { var result = _map[typeof(TEntity)] .FirstOrDefault(kv => kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) && (!canSortRequired || kv.Value.CanSort) && (!canFilterRequired || kv.Value.CanFilter)); return (result.Value?.FullName, result.Key); } catch (Exception ex) when (ex is KeyNotFoundException || ex is ArgumentNullException) { return (null, null); } } } }