LINQ group by query using reflected property name
Asked Answered
A

3

10

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;
    }
}
Aquileia answered 23/10, 2013 at 11:41 Comment(0)
E
25

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

Elector answered 23/10, 2013 at 11:55 Comment(1)
This worked nicely with one modification. The above code will return the property object, which if you group by the property name, you get everything returned under a single bucket with the label of the property name. To get this working, you need to make the group by clause ".GroupBy(c => c.GetType().GetProperty(category).GetValue(c, null))"Aquileia
B
5

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.

Brand answered 23/10, 2013 at 12:13 Comment(0)
H
0

If the count of categories for group are dynamic you can do this:

List<string> categories = new List<string>{ctegory_1,category_2...}
var grouped =_customerInterface.CustomerInterface.GetAllCustomers().GroupBy($"new({string.Join(",", categories)})", "it");
List<List<Customers>> customers = new List<List<Customers>>();
foreach (var item in grouped)
{
   var list = (item as Ienumerable<Customer>).ToList();
   customers.add(list);
}
Hesitate answered 22/7 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.