Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ group by query using reflected property name

I want to populate a drop down with the public properties of a particular object, which I have done fine. But now when the user selects the value from the dropdown, I want it to group the DB table results by that column. I have tried using LINQ but I can only figure out how to explicitly group by an instance variables property, not by a reflection property. This is my method - the parameter passed in is the string name of the property. Eg it will be "Country" if the user wants to group by Customer.Country, it will be "State" if the user wants to group by Customer.State. But at the moment I have hard coded to group by "State" as I cannot figure out how to use the string value passed in with my LINQ query

private void DisplayReportAction(string category)
{
    if (!string.IsNullOrEmpty(category))
    {
        SelectedCategory = category;
        _summaries.Clear();

        foreach (var custGroup in _customerInterface.CustomerInterface.GetAllCustomers().GroupBy(c => c.State)
            .Select(group => new
                                 {
                                     Category = group.Key,
                                     Count = group.Count()
                                 })
                                 .OrderBy(x => x.Category))
        {
            _summaries.Add(new CustomerReportSummaryViewModel(custGroup.Category, custGroup.Count));
        }

        ReportVisibility = Visibility.Visible;
    }
}
like image 997
NZJames Avatar asked Oct 23 '13 11:10

NZJames


2 Answers

You can use Reflection if you are using LINQ to Objects, for instance you can use this:

_customerInterface.CustomerInterface.GetAllCustomers()
     .GroupBy(c => c.GetType().GetProperty(category).GetValue(c, null))

If you are using Linq To Sql then an alternative is to use dynamic queries, check this link

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

like image 181
Esteban Elverdin Avatar answered Oct 23 '22 17:10

Esteban Elverdin


You may build expression dynamically:

    Expression<Func<Customer,T>> buildExpression<T>(string category)
    {
        //First build parameter of lambda ( 'c =>' part of expression)
        ParameterExpression param = Expression.Parameter(typeof(Customer), "c");
        //Then body of expression : ' => c.category'
        Expression<Func<Customer, T>> exp = Expression.Lambda<Func<Customer, T>>(Expression.Property(param, category), param);
        return exp;
    }

And finally, call

_customerInterface.CustomerInterface.GetAllCustomers()
    .GroupBy(buildExpression(category))

EDIT: Well, sorry you still have to know the type of property to give T type parameter to buildExpression function

There are ways to do this, using for example GetProperty(category).PropertyType and then call MakeGenericMethod on GetType().GetMethod("buildExpression<>"), but this requires a little more work. Anyway, you'll have to find a way to build CustomerReportSummaryViewModel from this type.

I don't know your use case, but you maybe all categories properties are of the same type, so you could hard-code it ?

If you are interested, and can't find a proper way to do it let me know I'll try to write a proper solution.

like image 24
Kek Avatar answered Oct 23 '22 19:10

Kek