diff --git a/Sieve/Services/SievePropertyMapper.cs b/Sieve/Services/SievePropertyMapper.cs index f9c1837..c3b8fc2 100644 --- a/Sieve/Services/SievePropertyMapper.cs +++ b/Sieve/Services/SievePropertyMapper.cs @@ -4,20 +4,19 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Text; namespace Sieve.Services { public class SievePropertyMapper { - private readonly Dictionary> _map - = new Dictionary>(); + private readonly Dictionary>> _map + = new Dictionary>>(); public PropertyFluentApi Property(Expression> expression) { if(!_map.ContainsKey(typeof(TEntity))) { - _map.Add(typeof(TEntity), new Dictionary()); + _map.Add(typeof(TEntity), new List>()); } return new PropertyFluentApi(this, expression); @@ -65,13 +64,16 @@ namespace Sieve.Services private void UpdateMap() { - _sievePropertyMapper._map[typeof(TEntity)][_property] = new SievePropertyMetadata() + 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) @@ -105,8 +107,8 @@ namespace Sieve.Services var result = _map[typeof(TEntity)] .FirstOrDefault(kv => kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) - && (canSortRequired ? kv.Value.CanSort : true) - && (canFilterRequired ? kv.Value.CanFilter : true)); + && (!canSortRequired || kv.Value.CanSort) + && (!canFilterRequired || kv.Value.CanFilter)); return (result.Value?.FullName, result.Key); } diff --git a/SieveUnitTests/Entities/Comment.cs b/SieveUnitTests/Entities/Comment.cs index e3418be..b914f28 100644 --- a/SieveUnitTests/Entities/Comment.cs +++ b/SieveUnitTests/Entities/Comment.cs @@ -1,5 +1,6 @@ using System; using Sieve.Attributes; +using SieveUnitTests.ValueObjects; namespace SieveUnitTests.Entities { @@ -7,6 +8,10 @@ namespace SieveUnitTests.Entities { public int Id { get; set; } + public Name AuthorFirstName { get; set; } + + public Name AuthorLastName { get; set; } + [Sieve(CanFilter = true, CanSort = true)] public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow; diff --git a/SieveUnitTests/General.cs b/SieveUnitTests/General.cs index 1a22fb0..1c5e96b 100644 --- a/SieveUnitTests/General.cs +++ b/SieveUnitTests/General.cs @@ -7,6 +7,7 @@ using Sieve.Models; using Sieve.Services; using SieveUnitTests.Entities; using SieveUnitTests.Services; +using SieveUnitTests.ValueObjects; namespace SieveUnitTests { @@ -63,17 +64,23 @@ namespace SieveUnitTests new Comment() { Id = 0, DateCreated = DateTimeOffset.UtcNow.AddDays(-20), - Text = "This is an old comment." + Text = "This is an old comment.", + AuthorFirstName = new Name("FirstName1"), + AuthorLastName = new Name("LastName1") }, new Comment() { Id = 1, DateCreated = DateTimeOffset.UtcNow.AddDays(-1), - Text = "This is a fairly new comment." + Text = "This is a fairly new comment.", + AuthorFirstName = new Name("FirstName2"), + AuthorLastName = new Name("LastName2") }, new Comment() { Id = 2, DateCreated = DateTimeOffset.UtcNow, - Text = "This is a brand new comment. ()" + Text = "This is a brand new comment. ()", + AuthorFirstName = new Name("FirstName3"), + AuthorLastName = new Name("LastName3") }, }.AsQueryable(); } @@ -365,5 +372,20 @@ namespace SieveUnitTests Assert.AreEqual(posts[2].Id, 2); Assert.AreEqual(posts[3].Id, 1); } + + [TestMethod] + public void NestedFilteringWithIdenticTypesWorks() + { + var model = new SieveModel() + { + Filters = "(firstName|lastName)@=*2", + }; + + var result = _processor.Apply(model, _comments); + Assert.AreEqual(1, result.Count()); + + var comment = result.First(); + Assert.AreEqual(comment.Id, 1); + } } } diff --git a/SieveUnitTests/Services/ApplicationSieveProcessor.cs b/SieveUnitTests/Services/ApplicationSieveProcessor.cs index 2910352..9f43617 100644 --- a/SieveUnitTests/Services/ApplicationSieveProcessor.cs +++ b/SieveUnitTests/Services/ApplicationSieveProcessor.cs @@ -31,6 +31,14 @@ namespace SieveUnitTests.Services mapper.Property(p => p.OnlySortableViaFluentApi) .CanSort(); + mapper.Property(c => c.AuthorFirstName.Value) + .CanFilter() + .HasName("firstName"); + + mapper.Property(c => c.AuthorLastName.Value) + .CanFilter() + .HasName("lastName"); + return mapper; } } diff --git a/SieveUnitTests/ValueObjects/Name.cs b/SieveUnitTests/ValueObjects/Name.cs new file mode 100644 index 0000000..58fa0e2 --- /dev/null +++ b/SieveUnitTests/ValueObjects/Name.cs @@ -0,0 +1,43 @@ +using System; + +namespace SieveUnitTests.ValueObjects +{ + public sealed class Name : IEquatable + { + public Name(string value) + { + if (string.IsNullOrEmpty(value)) + { + throw new InvalidOperationException("Invalid string!"); + } + + if (value.Length > 50) + { + throw new InvalidOperationException("String exceeds maximum name length!"); + } + + Value = value; + } + + public string Value { get; private set; } + + public bool Equals(Name other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Value, other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj is Name && Equals((Name) obj); + } + + public override int GetHashCode() + { + return (Value != null ? Value.GetHashCode() : 0); + } + } +}