• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 370
  • Last Modified:

Linq - Dynamic grouping

I'm creating several linq-query's that always return a list<GroupedInfo>.
Depending of the search-criteria, I need to add more or less grouping-fields.
Right now I perform this by using several procedures which contain a linq-query with the correct fields and mapping.

    public class GroupedInfo
    {
        public decimal DossierManagerId { get; set; }
        public string DossierManagerName { get; set; }
        public int MonthTransferDate { get; set; }
        public string QuestionType { get; set; }
        public int Counter { get; set; }
        public string QuestionStatus { get; set; }
    }

Open in new window

Is there any way to create a general method to pass fe a class as grouping-class and another item that tells how to map), so that I just need 1 method ?

Suppose I have the following linq-query's:

var query = (from table in managementInfoListDataTable.AsEnumerable()
                    group table by
                        new
                            {
                                mgrId = table.DossierManagerId,
                                mgrName = table.DossierManagerName,
                                MonthTransferDate = table.TransferDate.Month,
                                QuestionType = table.QuestionTypeCv,
                                QuestionStatus = table.StatusCv
                            }
                    into groupedTable
                    select new GroupedInfo
                               {
                                   DossierManagerId = groupedTable.Key.mgrId,
                                   DossierManagerName = groupedTable.Key.mgrName,
                                   MonthTransferDate = groupedTable.Key.MonthTransferDate,
                                   Counter = groupedTable.Count(),
                                   QuestionType = groupedTable.Key.QuestionType,
                                   QuestionStatus = groupedTable.Key.QuestionStatus
                        }).OrderBy(obj => obj.DossierManagerId).ThenBy(
                                                          obj => obj.DossierManagerName).ToList();

var query2 =             return (from table in managementInfoListDataTable.AsEnumerable()
                    group table by
                        new
                        {
                            MonthTransferDate =  table.TransferDate.Month
                        }
                        into groupedTable
                        select new GroupedInfo
                        {
                            MonthTransferDate = groupedTable.Key.MonthTransferDate,
                            Counter = groupedTable.Count(),
                        }).OrderBy(obj => obj.MonthTransferDate).ToList();

Open in new window



Can it become something like:

var query = (from table in managementInfoListDataTable.AsEnumerable()
                    group table by
                        GROUPINGCLASS
                    into groupedTable
                    select new GroupedInfo
                               {
                         MAPPING
                        }).OrderBy(obj => obj.DossierManagerId).ThenBy(
                                                          obj => obj.DossierManagerName).ToList();

Open in new window

Eventually a dynamic orderby off course
0
Dhaest
Asked:
Dhaest
  • 3
  • 3
1 Solution
 
Bob LearnedCommented:
There is a concept with LINQ that is useful when building dynamic queries.  It is called Composition, and it uses the nature of LINQ where execution is delayed.

Here is an article that talks about building composable queries:

Composable LINQ to SQL query with dynamic OrderBy
http://blogs.msdn.com/b/swiss_dpe_team/archive/2008/06/05/composable-linq-to-sql-query-with-dynamic-orderby.aspx

The trick is to be careful, and not do something that causes the query to execute (such as ToList)
0
 
DhaestAuthor Commented:
I'm not using linq-to-sql. I need to query a datatable.

The main part I'm looking for is the dynamic part of the grouping
0
 
Bob LearnedCommented:
Aah, yes, the reference threw you off.  That was meant to show how LINQ is composable, not LINQ-to-SQL.  That was the first article that I could find that explained that concept.  I am trying to find a previous reference article that would explain this a lot better than that article does, but I am not having a lot of luck.

If you build the pieces one step at a time, you can combine the results of one query with another, and it won't be executed until you need it to be.  This concept is best for a dynamic where clause, and I haven't seen it done with grouping, but I don't see why it can't work if you careful construct the different parts.
0
Free recovery tool for Microsoft Active Directory

Veeam Explorer for Microsoft Active Directory provides fast and reliable object-level recovery for Active Directory from a single-pass, agentless backup or storage snapshot — without the need to restore an entire virtual machine or use third-party tools.

 
Bob LearnedCommented:
In the meantime, here are some reading materials:

1) LINQ queries are composable
http://www.careerride.com/LINQ-queries-are-composable.aspx

2) Composable and Efficient LINQ Queries
http://destructothedevilcat.wordpress.com/2009/02/15/composable-and-efficient-linq-queries/
0
 
DhaestAuthor Commented:
I finally solved it by using the following extension-method and by using reflection to fill the correct properties of the object


       public static Collection<GroupedInfo> ReturnAsGroupedInfo<TKey, TElement>(this
IEnumerable<IGrouping<TKey, TElement>> elements, string groupByProperty)
        {
            Collection<GroupedInfo> groupedInfoList = new Collection<GroupedInfo>();
            string[] propertyNames = groupByProperty.Split(',');            
            PropertyInfo prop = null;

            foreach (var element in elements)
            {
                GroupedInfo groupedInfo = new GroupedInfo();
                string[] keys = element.Key.ToString().Split(',');
                for (int counter = 0; counter <= keys.GetUpperBound(0); counter++)
                {
                    prop = groupedInfo.GetType().GetProperty(propertyNames[counter], BindingFlags.Public | BindingFlags.Instance);
                    if (null != prop && prop.CanWrite)
                    {
                        prop.SetValue(groupedInfo, Convert.ChangeType(keys[counter], Type.GetType(prop.PropertyType.FullName)), null);
                    }
                }

                prop = groupedInfo.GetType().GetProperty("Counter", BindingFlags.Public | BindingFlags.Instance);
                prop.SetValue(groupedInfo, element.Count(), null);

                groupedInfoList.Add(groupedInfo);
            }


            return groupedInfoList;
        }

Open in new window


Using the following code to call it:

 string mapToKeyProperty = "...";
groupedInfoList = managementInfoListDataTable.GroupBy(groupingFunction).ReturnAsGroupedInfo(mapToKeyProperty)

Open in new window

0
 
DhaestAuthor Commented:
No alternatives found
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now