From 390ed34f7084dbdd9c8adeb7657c6bbfce9eaf4b Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Thu, 2 May 2019 12:26:55 -0600 Subject: [PATCH] Support dotted identifier property expressions. Resolves #1 --- Example/Controllers/CustomersController.cs | 13 +++--- Example/Models/Customer.cs | 30 ++++++++++++- F23.ODataLite/F23.ODataLite.csproj | 2 +- .../Internal/ExpressionQueryTokenVisitor.cs | 45 ++++++++++++++++++- 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/Example/Controllers/CustomersController.cs b/Example/Controllers/CustomersController.cs index 02d8c6a..2a7b541 100644 --- a/Example/Controllers/CustomersController.cs +++ b/Example/Controllers/CustomersController.cs @@ -13,18 +13,19 @@ public class CustomersController : ControllerBase { // Names generated from http://random-name-generator.info/random/?n=10&g=1&st=2 // Ages generated from https://www.random.org/integers/?num=10&min=18&max=100&col=5&base=10&format=html&rnd=new + // Loyalty program member numbers generated from https://www.random.org/integers/?num=10&min=100000&max=1000000&col=5&base=10&format=html&rnd=new // Any similarity to persons real or fictional is coincidental! private static readonly Customer[] _customers = { - new Customer(1, "Daryl", "Barber", 73, true), - new Customer(2, "Cheryl", "Jimenez", 54, true), + new Customer(1, "Daryl", "Barber", 73, true, 832373, CustomerLoyaltyProgramLevel.Gold), + new Customer(2, "Cheryl", "Jimenez", 54, true, 775751, CustomerLoyaltyProgramLevel.Silver), new Customer(3, "Caleb", "Mitchell", 29, false), - new Customer(4, "Joanne", "Griffin", 61, true), + new Customer(4, "Joanne", "Griffin", 61, true, 534424, CustomerLoyaltyProgramLevel.Bronze), new Customer(5, "Irma", "Holloway", 40, false), - new Customer(6, "Arthur", "Sandoval", 64, true), - new Customer(7, "Tracy", "Frank", 45, true), + new Customer(6, "Arthur", "Sandoval", 64, true, 229658, CustomerLoyaltyProgramLevel.Gold), + new Customer(7, "Tracy", "Frank", 45, true, 860876, CustomerLoyaltyProgramLevel.Silver), new Customer(8, "Sherman", "Drake", 89, false), - new Customer(9, "Gwen", "Brown", 48, true), + new Customer(9, "Gwen", "Brown", 48, true, 373442, CustomerLoyaltyProgramLevel.Bronze), new Customer(10, "Melvin", "Woods", 54, false) }; diff --git a/Example/Models/Customer.cs b/Example/Models/Customer.cs index 611d961..2e2f78f 100644 --- a/Example/Models/Customer.cs +++ b/Example/Models/Customer.cs @@ -1,4 +1,6 @@ using F23.Hateoas; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Example.Models { @@ -8,13 +10,22 @@ public Customer() { } - public Customer(int id, string firstName, string lastName, int age, bool isPreferred) + public Customer(int id, string firstName, string lastName, int age, bool isPreferred, int? memberNumber = null, CustomerLoyaltyProgramLevel? loyaltyLevel = null) { Id = id; FirstName = firstName; LastName = lastName; Age = age; IsPreferred = isPreferred; + + if (memberNumber.HasValue && loyaltyLevel.HasValue) + { + LoyaltyProgram = new CustomerLoyaltyProgram + { + MemberNumber = memberNumber.Value, + Level = loyaltyLevel.Value + }; + } } public int Id { get; set; } @@ -26,5 +37,22 @@ public Customer(int id, string firstName, string lastName, int age, bool isPrefe public int Age { get; set; } public bool IsPreferred { get; set; } + + public CustomerLoyaltyProgram LoyaltyProgram { get; set; } + } + + public class CustomerLoyaltyProgram + { + public int MemberNumber { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public CustomerLoyaltyProgramLevel Level { get; set; } + } + + public enum CustomerLoyaltyProgramLevel + { + Bronze, + Silver, + Gold } } diff --git a/F23.ODataLite/F23.ODataLite.csproj b/F23.ODataLite/F23.ODataLite.csproj index ae1e8be..852d619 100644 --- a/F23.ODataLite/F23.ODataLite.csproj +++ b/F23.ODataLite/F23.ODataLite.csproj @@ -12,7 +12,7 @@ odata asp.net core lite MIT 2019, feature[23] - 1.1.1 + 1.2.0 diff --git a/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs b/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs index e74bf4c..d2f0054 100644 --- a/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs +++ b/F23.ODataLite/Internal/ExpressionQueryTokenVisitor.cs @@ -87,7 +87,50 @@ public Expression Visit(InToken tokenIn) public Expression Visit(DottedIdentifierToken tokenIn) { - throw new NotImplementedException(); + var parts = tokenIn.Identifier.Split('.'); + + var propExprs = new List(); + + var prop = _properties.FirstOrDefault(i => i.Name.Equals(parts[0], StringComparison.OrdinalIgnoreCase)); + + if (prop == null) + throw new InvalidOperationException($"Property {parts[0]} not found on type {_parameter.Type.Name}"); + + var nullExpr = Expression.Constant(null); + + var propExpr = Expression.Property(_parameter, prop); + propExprs.Add(propExpr); + + for (int i = 1; i < parts.Length; i++) + { + var parentType = prop.PropertyType; + var props = parentType.GetProperties(BindingFlags.Instance | BindingFlags.Public); + prop = props.FirstOrDefault(p => p.Name.Equals(parts[i], StringComparison.OrdinalIgnoreCase)); + + if (prop == null) + throw new InvalidOperationException($"Property {parts[i]} not found on type {parentType.Name}"); + + propExpr = Expression.Property(propExpr, prop); + propExprs.Add(propExpr); + } + + var finalType = prop.PropertyType; + Expression expr = propExprs[propExprs.Count - 1]; + + if (propExprs.Count > 1) + { + for (int i = propExprs.Count - 2; i >= 0; i--) + { + propExpr = propExprs[i]; + + expr = Expression.Condition( + Expression.Equal(Expression.Convert(propExpr, typeof(object)), nullExpr), + Expression.Default(finalType), + expr); + } + } + + return expr; } public Expression Visit(ExpandToken tokenIn)