using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Sieve.Models; namespace Sieve.Services { public class SievePropertyMapper { private readonly Dictionary<Type, ICollection<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>> _map = new Dictionary<Type, ICollection<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>>(); public PropertyFluentApi<TEntity> Property<TEntity>(Expression<Func<TEntity, object>> expression) { if (!_map.ContainsKey(typeof(TEntity))) { _map.Add(typeof(TEntity), new List<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>()); } return new PropertyFluentApi<TEntity>(this, expression); } public class PropertyFluentApi<TEntity> { private readonly SievePropertyMapper _sievePropertyMapper; private readonly PropertyInfo _property; public PropertyFluentApi(SievePropertyMapper sievePropertyMapper, Expression<Func<TEntity, object>> 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<TEntity> CanFilter() { _canFilter = true; UpdateMap(); return this; } public PropertyFluentApi<TEntity> CanSort() { _canSort = true; UpdateMap(); return this; } public PropertyFluentApi<TEntity> 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<PropertyInfo, ISievePropertyMetadata>(_property, metadata); _sievePropertyMapper._map[typeof(TEntity)].Add(pair); } private static (string, PropertyInfo) GetPropertyInfo(Expression<Func<TEntity, object>> 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<string>(); while (body != null) { stack.Push(body.Member.Name); body = body.Expression as MemberExpression; } return (string.Join(".", stack.ToArray()), member); } } public (string, PropertyInfo) FindProperty<TEntity>( 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); } } } }