Skip to content

Commit

Permalink
Adding support for nested properties with depth more than one (issues #…
Browse files Browse the repository at this point in the history
…29 and #26).
  • Loading branch information
dbelmont committed Jan 18, 2019
1 parent 95ab61e commit b9a0ae1
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 15 deletions.
65 changes: 63 additions & 2 deletions ExpressionBuilder.Test/Integration/BuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ public List<Person> People
}
}

private List<Company> _companies;

public List<Company> Companies
{
get
{
if (_companies == null)
{
_companies = new TestData().Companies;
}

return _companies;
}
}

[TestCase(TestName = "Build expression from an empty filter: should return all records")]
public void BuilderWithEmptyFilter()
{
Expand Down Expand Up @@ -173,7 +188,7 @@ public void BuilderUsingIsNotNullOperationOnAnInnerProperty()
public void BuilderUsingIsEmptyOperation()
{
var filter = new Filter<Person>();
filter.By("Birth.Country", Operation.IsEmpty, (object)null, (object)null, Connector.And);
filter.By("Birth.Country", Operation.IsEmpty, null, (object)null, Connector.And);
var people = People.Where(filter);
var solution = People.Where(p => p.Birth != null && p.Birth.Country != null && p.Birth.Country.Trim() == string.Empty);
Assert.That(people, Is.EquivalentTo(solution));
Expand Down Expand Up @@ -333,9 +348,55 @@ public void PropertyValueTypeMismatchWithOneValue()
public void PropertyValueTypeMismatchWithTwoValues()
{
var filter = new Filter<Person>();
filter.By("Id", Operation.Between, (int)1, 7700000000000007D);
filter.By("Id", Operation.Between, 1, 7700000000000007D);
var ex = Assert.Throws<PropertyValueTypeMismatchException>(() => People.Where(filter));
ex.Message.Should().Be("The type of the member 'Id' (Int32) is different from the type of one of the constants (Double)");
}

[TestCase(TestName = "Nested property with depth of two", Category = "NestedProperties")]
public void NestedPropertyDepthTwo()
{
var filter = new Filter<Person>();
filter.By("Manager.Birth.Country", Operation.EqualTo, "USA");
var people = People.Where(filter);
var solution = People.Where(p => p.Manager != null && p.Manager.Birth != null && p.Manager.Birth.Country == "USA");

Assert.That(people, Is.EquivalentTo(solution));
}

[TestCase(TestName = "Nested property with depth of three", Category = "NestedProperties")]
public void NestedPropertyDepthThree()
{
var filter = new Filter<Person>();
filter.By("Manager.Employer.Owner.Name", Operation.Contains, "smith");
var people = People.Where(filter);
var solution = People.Where(p => p.Manager != null && p.Manager.Employer != null
&& p.Manager.Employer.Owner.Name.Trim().ToLower().Contains("smith"));

Assert.That(people, Is.EquivalentTo(solution));
}

[TestCase(TestName = "Nested list property with depth of two", Category = "NestedProperties")]
public void NestedListPropertyDepthTwo()
{
var filter = new Filter<Company>();
filter.By("Managers[Birth.Country]", Operation.EqualTo, "USA");
var companies = Companies.Where(filter);
var solution = Companies.Where(c => c.Managers.Any(p => p.Birth != null && p.Birth.Country == "USA"));

Assert.That(companies, Is.EquivalentTo(solution));
}

[TestCase(TestName = "Nested list property with depth of three", Category = "NestedProperties")]
public void NestedListPropertyDepthThree()
{
var filter = new Filter<Company>();
filter.By("Managers[Employer.Owner.Name]", Operation.Contains, "smith");
var companies = Companies.Where(filter);
var solution = Companies.Where(c => c.Managers.Any(p => p.Employer != null
&& p.Employer.Owner.Name.Trim().ToLower().Contains("smith")));

Assert.That(companies, Is.EquivalentTo(solution));
}
}
}
19 changes: 15 additions & 4 deletions ExpressionBuilder.Test/Models/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class Person
public BirthData Birth { get; set; }
public List<Contact> Contacts { get; private set; }
public Company Employer { get; set; }
public Person Manager { get; set; }
public double Salary { get; set; }

public Person()
Expand Down Expand Up @@ -69,7 +70,10 @@ public int Age
{
get
{
if (!Date.HasValue) return 0;
if (!Date.HasValue)
{
return 0;
}

var timeSpan = DateTime.Now - Date.Value;
return (int)(timeSpan.Days / 365.2425);
Expand All @@ -81,11 +85,18 @@ public override string ToString()
return string.Format("Born at {0} in {1} ({2} yo)", Date.Value.ToShortDateString(), Country, Age);
}
}
}

public class Company
{
public string Name { get; set; }
public string Industry { get; set; }
public Person Owner { get; set; }
public List<Person> Managers { get; set; }

public class Company
public Company()
{
public string Name { get; set; }
public string Industry { get; set; }
Managers = new List<Person>();
}
}
}
26 changes: 21 additions & 5 deletions ExpressionBuilder.Test/Unit/Helpers/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,34 @@ public class TestData
{
public List<Person> People { get; private set; }

public List<Company> Companies { get; private set; }

public TestData()
{
var company = new Person.Company { Name = "Back to the future", Industry = "Time Traveling Agency" };
var owner = new Person { Name = "John Smith", Gender = PersonGender.Male, Birth = new Person.BirthData { Date = new DateTime(1965, 8, 28), Country = "USA" } };

var company = new Company { Name = "Back to the future", Industry = "Time Traveling Agency", Owner = owner };

var manager = new Person { Name = "Bob Storm", Birth = new Person.BirthData { Date = new DateTime(1970, 11, 9), Country = "USA" }, Employer = company };
var manager2 = new Person { Name = "Ben Clark", Birth = new Person.BirthData { Date = new DateTime(1972, 3, 12), Country = "AUS" } };
company.Managers = new List<Person> { manager, manager2 };

Companies = new List<Company>
{
company,
new Company { Name = "Acme Inc." },
new Company { Name = "Underground Ltd.", Managers = new List<Person> { manager2 }},
new Company { Name = "Beyond Co.", Managers = new List<Person> { manager }},
};

People = new List<Person>
{
new Person { Name = "John Doe", Gender = PersonGender.Male, Salary=4565, Birth = new Person.BirthData { Date = new DateTime(1979, 2, 28) }, Employer = company },
new Person { Name = "John Doe", Gender = PersonGender.Male, Salary=4565, Birth = new Person.BirthData { Date = new DateTime(1979, 2, 28) }, Employer = company, Manager = manager2 },
new Person { Name = "Jane Doe", Gender = PersonGender.Female, Salary=4973, Birth = new Person.BirthData { Date = new DateTime(1985, 9, 5), Country = " " } },
new Person { Name = "Wade Wilson", Gender = PersonGender.Male, Salary=3579, Birth = new Person.BirthData { Date = new DateTime(1973, 10, 9), Country = "USA" } },
new Person { Name = "Jessica Jones", Gender = PersonGender.Female, Salary=5000, Birth = new Person.BirthData { Date = new DateTime(1980, 12, 20), Country = "usa" } },
new Person { Name = "Wade Wilson", Gender = PersonGender.Male, Salary=3579, Birth = new Person.BirthData { Date = new DateTime(1973, 10, 9), Country = "USA" }, Manager = manager },
new Person { Name = "Jessica Jones", Gender = PersonGender.Female, Salary=5000, Birth = new Person.BirthData { Date = new DateTime(1980, 12, 20), Country = "usa" }, Manager = manager },
new Person { Name = "Jane Jones", Gender = PersonGender.Female, Salary=3500, Birth = new Person.BirthData { Date = new DateTime(1980, 12, 20), Country = "AUS" } },
new Person { Name = "Fulano Silva", Gender = PersonGender.Male, Salary=3322, Birth = new Person.BirthData { Date = new DateTime(1983, 5, 10), Country = "BRA" }, Employer = company },
new Person { Name = "Fulano Silva", Gender = PersonGender.Male, Salary=3322, Birth = new Person.BirthData { Date = new DateTime(1983, 5, 10), Country = "BRA" }, Employer = company, Manager = manager2 },
new Person { Name = "John Hancock", Gender = PersonGender.Male, Employer = company }
};
var id = 1;
Expand Down
9 changes: 6 additions & 3 deletions ExpressionBuilder/Builders/FilterBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,11 @@ public Expression GetSafePropertyMember(ParameterExpression param, string member
return expr;
}

var parentMember = GetParentMember(param, memberName);
return Expression.AndAlso(Expression.NotEqual(parentMember, Expression.Constant(null)), expr);
var index = memberName.LastIndexOf(".");
var parentName = memberName.Substring(0, index);
var subParam = param.GetMemberExpression(parentName);
var resultExpr = Expression.AndAlso(Expression.NotEqual(subParam, Expression.Constant(null)), expr);
return GetSafePropertyMember(param, parentName, resultExpr);
}

protected Expression CheckIfParentIsNull(ParameterExpression param, string memberName)
Expand All @@ -154,7 +157,7 @@ protected Expression CheckIfParentIsNull(ParameterExpression param, string membe
return Expression.Equal(parentMember, Expression.Constant(null));
}

private Expression GetParentMember(ParameterExpression param, string memberName)
private MemberExpression GetParentMember(ParameterExpression param, string memberName)
{
var parentName = memberName.Substring(0, memberName.IndexOf("."));
return param.GetMemberExpression(parentName);
Expand Down
2 changes: 1 addition & 1 deletion ExpressionBuilder/Resources/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace ExpressionBuilder.Resources
public class Property
{
/// <summary>
/// Property identifier conventionalized by for the Expression Builder.
/// Property identifier conventionalized by the Expression Builder.
/// </summary>
public string Id { get; private set; }

Expand Down
10 changes: 10 additions & 0 deletions ExpressionBuilder/Resources/PropertyCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class PropertyCollection : IPropertyCollection
/// </summary>
public ResourceManager ResourceManager { get; private set; }

private readonly HashSet<Type> _visitedTypes;

private List<Property> Properties { get; set; }

/// <summary>
Expand Down Expand Up @@ -54,6 +56,7 @@ public class PropertyCollection : IPropertyCollection
public PropertyCollection(Type type)
{
Type = type;
_visitedTypes = new HashSet<Type>();
Properties = LoadProperties(Type);
}

Expand Down Expand Up @@ -94,6 +97,13 @@ private string GetPropertyResourceName(string propertyConventionName)
private List<Property> LoadProperties(Type type)
{
var list = new List<Property>();
if (_visitedTypes.Contains(type))
{
return list;
}

_visitedTypes.Add(type);

const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
MemberInfo[] members = type.GetFields(bindingFlags).Cast<MemberInfo>()
.Concat(type.GetProperties(bindingFlags)).ToArray();
Expand Down

0 comments on commit b9a0ae1

Please sign in to comment.