amhed
amhed

Reputation: 3659

Method that receives LINQ IGrouping as Parameter

Edit 3:Improved question wording and examples

I have the following linq query that uses grouping. The grouping and select operations are complex, so I abstracted one of the selects to a method that makes some choices on how to render the data.

My query works correctly inside the anonymous group definition, but as soon as I type it to a class in order to pass it to a method as an IGrouping object it stops grouping the results.

public class TestController : Controller
{
    public JsonResult ThisWorks()
    {
        var valueList = DataMocker.GetTestValues();

        var group = from v in valueList.AsEnumerable()
                    where (v.Data != 0)
                    group v by new 
                                   {
                                       Year = v.Fecha.Value.Year,
                                       Trimester = string.Empty,
                                       Month = v.Fecha.Value.Month,
                                       Day = 0,
                                   }
                        into g
                        select new SeriesDataPoint
                                                     {
                                                         y = g.OrderByDescending(obd => obd.Fecha)
                                                                  .Select(obd => obd.Data.Value)
                                                                  .FirstOrDefault(),

                                                         color = "black",
                                                         month = g.Key.Month,
                                                         year = g.Key.Year,
                                                         seriesName = "Test Series",
                                                     };

        return Json(group, JsonRequestBehavior.AllowGet);
    }

    public JsonResult ThisDoesnt()
    {
        var valueList = DataMocker.GetTestValues();

        var group = from v in valueList.AsEnumerable()
                    where (v.Data != 0)
                    group v by new Models.SeriesResultGroup
                    {
                        Year = v.Fecha.Value.Year,
                        Trimester = string.Empty,
                        Month = v.Fecha.Value.Month,
                        Day = 0,
                    }
                        into g
                        select new SeriesDataPoint
                        {
                            y = RenderDataPoint(valueList, g),
                            color = "black",
                            month = g.Key.Month,
                            year = g.Key.Year,
                            seriesName = "Test Series",
                        };

        return Json(group, JsonRequestBehavior.AllowGet);
    }

    public static decimal? RenderDataPoint(List<Models.ValoresResultSet> valores, IGrouping<Models.SeriesResultGroup, Models.ValoresResultSet> group)
    {
        return group.OrderByDescending(obd => obd.Fecha)
                    .Select(obd => obd.Data.Value)
                    .FirstOrDefault();
    }
}

Upvotes: 0

Views: 2059

Answers (1)

Oleh Nechytailo
Oleh Nechytailo

Reputation: 2195

In first case you group by anonymous type, generated by compiler. This type also has generated Equals and HashCode overrides (you can check it via ildasm). Anonymous type`s default Equals runs equality comparer for each field. I think this was made for use in cases like this.

In second case you group by your custom type. Since it is a reference type, default equality comparer compares objects by reference. Because before grouping you produce a sequence of objects, each of them is unique. So default equality check thinks that they differs.

Solutions are (choose any):

  1. Override Equals and HashCode.
  2. Make type struct instead of class

Be careful and don`t forget to implement HashCode as well.

Upvotes: 2

Related Questions