Merge pull request #54 from radeanurazvan/master

Allowed configuring properties with identical name & type
This commit is contained in:
Biarity 2019-03-18 08:04:49 +10:00 committed by GitHub
commit d4b85b6bbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 10 deletions

View File

@ -4,20 +4,19 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
namespace Sieve.Services namespace Sieve.Services
{ {
public class SievePropertyMapper public class SievePropertyMapper
{ {
private readonly Dictionary<Type, Dictionary<PropertyInfo, ISievePropertyMetadata>> _map private readonly Dictionary<Type, ICollection<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>> _map
= new Dictionary<Type, Dictionary<PropertyInfo, ISievePropertyMetadata>>(); = new Dictionary<Type, ICollection<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>>();
public PropertyFluentApi<TEntity> Property<TEntity>(Expression<Func<TEntity, object>> expression) public PropertyFluentApi<TEntity> Property<TEntity>(Expression<Func<TEntity, object>> expression)
{ {
if(!_map.ContainsKey(typeof(TEntity))) if(!_map.ContainsKey(typeof(TEntity)))
{ {
_map.Add(typeof(TEntity), new Dictionary<PropertyInfo, ISievePropertyMetadata>()); _map.Add(typeof(TEntity), new List<KeyValuePair<PropertyInfo, ISievePropertyMetadata>>());
} }
return new PropertyFluentApi<TEntity>(this, expression); return new PropertyFluentApi<TEntity>(this, expression);
@ -65,13 +64,16 @@ namespace Sieve.Services
private void UpdateMap() private void UpdateMap()
{ {
_sievePropertyMapper._map[typeof(TEntity)][_property] = new SievePropertyMetadata() var metadata = new SievePropertyMetadata()
{ {
Name = _name, Name = _name,
FullName = _fullName, FullName = _fullName,
CanFilter = _canFilter, CanFilter = _canFilter,
CanSort = _canSort 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) private static (string, PropertyInfo) GetPropertyInfo(Expression<Func<TEntity, object>> exp)
@ -105,8 +107,8 @@ namespace Sieve.Services
var result = _map[typeof(TEntity)] var result = _map[typeof(TEntity)]
.FirstOrDefault(kv => .FirstOrDefault(kv =>
kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)
&& (canSortRequired ? kv.Value.CanSort : true) && (!canSortRequired || kv.Value.CanSort)
&& (canFilterRequired ? kv.Value.CanFilter : true)); && (!canFilterRequired || kv.Value.CanFilter));
return (result.Value?.FullName, result.Key); return (result.Value?.FullName, result.Key);
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using Sieve.Attributes; using Sieve.Attributes;
using SieveUnitTests.ValueObjects;
namespace SieveUnitTests.Entities namespace SieveUnitTests.Entities
{ {
@ -7,6 +8,10 @@ namespace SieveUnitTests.Entities
{ {
public int Id { get; set; } public int Id { get; set; }
public Name AuthorFirstName { get; set; }
public Name AuthorLastName { get; set; }
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;

View File

@ -7,6 +7,7 @@ using Sieve.Models;
using Sieve.Services; using Sieve.Services;
using SieveUnitTests.Entities; using SieveUnitTests.Entities;
using SieveUnitTests.Services; using SieveUnitTests.Services;
using SieveUnitTests.ValueObjects;
namespace SieveUnitTests namespace SieveUnitTests
{ {
@ -63,17 +64,23 @@ namespace SieveUnitTests
new Comment() { new Comment() {
Id = 0, Id = 0,
DateCreated = DateTimeOffset.UtcNow.AddDays(-20), 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() { new Comment() {
Id = 1, Id = 1,
DateCreated = DateTimeOffset.UtcNow.AddDays(-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() { new Comment() {
Id = 2, Id = 2,
DateCreated = DateTimeOffset.UtcNow, 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(); }.AsQueryable();
} }
@ -365,5 +372,20 @@ namespace SieveUnitTests
Assert.AreEqual(posts[2].Id, 2); Assert.AreEqual(posts[2].Id, 2);
Assert.AreEqual(posts[3].Id, 1); 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);
}
} }
} }

View File

@ -31,6 +31,14 @@ namespace SieveUnitTests.Services
mapper.Property<Post>(p => p.OnlySortableViaFluentApi) mapper.Property<Post>(p => p.OnlySortableViaFluentApi)
.CanSort(); .CanSort();
mapper.Property<Comment>(c => c.AuthorFirstName.Value)
.CanFilter()
.HasName("firstName");
mapper.Property<Comment>(c => c.AuthorLastName.Value)
.CanFilter()
.HasName("lastName");
return mapper; return mapper;
} }
} }

View File

@ -0,0 +1,43 @@
using System;
namespace SieveUnitTests.ValueObjects
{
public sealed class Name : IEquatable<Name>
{
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);
}
}
}