1

I have a need to use a delimited string in order by. EG "Product.Reference".

I seem to be having trouble as the result is ordered the same way it was before the method was called.

For example I have this xUnit test that shows my issue. The asserts show that the order is still the same.

EDIT: to be clear, I am not testing Order by, but the method PathToProperty. This test is for demonstration purposes only.

As you can see from the test I am using reflection in method private static object PathToProperty(object t, string path) So I am assuming I am doing something wrong in there?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit;

namespace Pip17PFinanceApi.Tests.Workers
{
    public class InputViewModel
    {
        public List<OrderViewModel> OrderViewModels { get; set; }
    }

    public class OrderViewModel
    {
        public Product Product { get; set; }
        public decimal Price { get; set; }
    }

    public class Product
    {
        public string Description { get; set; }
        public string Reference { get; set; }
    }

    public class OrderByWithReflection
    {
        [Fact]
        public void OrderByTest()
        {
            //Arrrange
            var model = new InputViewModel
            {
                OrderViewModels = new List<OrderViewModel>
                    {
                        new OrderViewModel{
                            Product = new Product
                            {
                                Reference = "02"
                            }
                        },
                        new OrderViewModel{
                            Product = new Product
                            {
                                Reference = "03"
                            }
                        },
                        new OrderViewModel{
                            Product = new Product
                            {
                                Reference = "01"
                            }
                        },
                        new OrderViewModel{
                            Product = new Product
                            {
                                Reference = "04"
                            }
                        },
                    }
            };

            //Act
            var query = model.OrderViewModels.OrderBy(t => PathToProperty(t, "Product.Reference"));
            var result = query.ToList();

            //Assert
            Assert.Equal("01", result[0].Product.Reference);
            Assert.Equal("02", result[1].Product.Reference);
            Assert.Equal("03", result[2].Product.Reference);
            Assert.Equal("04", result[3].Product.Reference);
        }

        private static object PathToProperty(object t, string path)
        {
            Type currentType = t.GetType();
            foreach (string propertyName in path.Split('.'))
            {
                PropertyInfo property = currentType.GetProperty(propertyName);
                t = property;
                currentType = property.PropertyType;
            }
            return t;
        }
    }
}
Mark Johnson
  • 575
  • 4
  • 20
  • 1
    Does this answer your question? [How do I specify the Linq OrderBy argument dynamically?](https://stackoverflow.com/questions/7265186/how-do-i-specify-the-linq-orderby-argument-dynamically) – jjxtra Dec 18 '20 at 18:05
  • Sorry I do not understand why are you testing `OrderBy` behavior? That method is from .net and it is tested by them. Or you product will sort the ViewModel entries? In that case you would have to call some API which sorts the ViewModel and verify the result in test. – Piyush Parashar Dec 18 '20 at 18:06
  • Your test seems to indicate the `OrderBy` is working, unless the `Assert`s are throwing... – Heretic Monkey Dec 18 '20 at 18:07
  • If the order were the same, the `Assert`s would have to be ` Assert.Equal("02", result[0].Product.Reference); Assert.Equal("03", result[1].Product.Reference); Assert.Equal("01", result[2].Product.Reference); Assert.Equal("04", result[3].Product.Reference); ` – Heretic Monkey Dec 18 '20 at 18:10
  • to be clear, I am not testing Order by, but the method PathToProperty. Really the test was just a demonstration of the issue. – Mark Johnson Dec 18 '20 at 20:45

1 Answers1

2

Your PathToProperty isn't correct. Think about it's return value - the last time through, you set t = property and then return t. But property is a PropertyInfo, so you are just comparing identical objects in the OrderBy.

I have a similar extension method I use:

public static object GetPropertyPathValue(this object curObject, string propsPath) {
    foreach (var propName in propsPath.Split('.'))
        curObject = curObject.GetType().GetProperty(propName).GetValue(curObject);

    return curObject;
}

If used in place of your PathToProperty method, the OrderBy will work.

var query = model.OrderViewModels.OrderBy(t => t.GetPropertyPathValue("Product.Reference"));

You could update your method to be something like:

private static object PathToProperty(object curObject, string path) {
    foreach (string propertyName in path.Split('.')) {
        var property = curObject.GetType().GetProperty(propertyName);
        curObject = property.GetValue(curObject);
    }
    return curObject;
}

for the same result.

PS Actually, using some other extension methods and LINQ, my normal method handles properties or fields:

public static object GetPathValue(this object curObject, string memberPath)
    => memberPath.Split('.').Aggregate(curObject, (curObject, memberName) => curObject.GetType().GetPropertyOrField(memberName).GetValue(curObject));
NetMage
  • 26,163
  • 3
  • 34
  • 55