5

Using System.Dynamic.Linq I have a group by statement that looks like this:

var rowGrouped = data.GroupBy(rGroup, string.Format("new({0})", c));

Where c is just a string array of field names I need to have selected in my grouping. GroupBy in this case returns a IEnumerable (note: not an IEnumerable<T> because we don't know what T is). The regular GroupBy would return a System.Collections.Generic.IEnumerable<IGrouping<TKey, TElement>>

Now my question is, how do I iterate through the groups such that I have access to the key (Key is defined in IGrouping<TKey, TElement> but I don't know TKey and TElement a priori)?

I initially tried this:

foreach (var row in rowGrouped)

Which iterates, but I can't access row.Key (row in this case is type object)

So I did this:

foreach (IGrouping<object,dynamic> row in rowGrouped)

Which, surprisingly, worked...as long as the key was a string. If, however, the key happened to be numeric (for example short), then I get this error:

Unable to cast object of type 'Grouping`2[System.Int16,DynamicClass2]' to type 
'System.Linq.IGrouping`2[System.Object,System.Object]'.

I need to be able to iterate through rowGrouped and get the Key for every row and then iterate through the collection in each group.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171

1 Answers1

3

The problem is that your key is a short and you're trying to cast to an interface where the key is object. While IGrouping<> has covariant type parameters, it just simply won't work because variance only applies to reference types. Since short is a value type, it will not work.

If you know the key will always be a short, you should cast it as such.

foreach (IGrouping<short, dynamic> row in rowGrouped)
    ...

Otherwise if it's possible that it can be a value type or a reference type, it might just be easier to keep the entire row as dynamic.

foreach (dynamic row in rowGrouped)
    ...

Though I personally have had troubles with this. Perhaps a bug but the runtime cannot figure out that the row has a Key property in this case. I can't tell you why exactly but keep that in mind.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • For more information, see: [Why covariance and contravariance do not support value type](http://stackoverflow.com/a/12454932/390278) – Jeff Mercado Sep 24 '14 at 16:32
  • The problem is that I don't know that the key will always be a short. Sometime it is, sometimes its a string or something else. – Matt Burland Sep 24 '14 at 16:46
  • The problem with `foreach (dynamic row in rowGrouped)` is that I get `RuntimeBinderException` with the message `'object' does not contain a definition for 'Key')` which is weird, because I can see `Key` when I hover over `row` in the debugger. – Matt Burland Sep 24 '14 at 16:50
  • I don't have a lot of context for how you will be using your queries but you'll have to come up with a way to make the key always a reference type then. You could do something like `data.GroupBy(string.Format("new({0} as Key)", rGroup), string.Format("new({0})", c))` when you group, then you just have to remember that the actual key is in the `Key` property of the `Key`. There are of course other ways you can deal with this, but this will probably be the simplest to implement. – Jeff Mercado Sep 24 '14 at 16:55
  • Actually that's pretty much the solution I'm stumbling to anyway. I have a related question, that was pretty much tackling the same problem from a different perspective and there are actually two fields, one is `year` and one is `qtr` that I wanted to combine into a single column. I think select them as `new (year,qtr)` actually fixes both problems and I can leave correctly formatting that object into `yearQqt` as a problem for the front-end. – Matt Burland Sep 24 '14 at 17:07
  • So I see why it fails if we use `dynamic` for the row, for the reasons [explined here](http://stackoverflow.com/questions/15338429/c-sharp-dynamic-type-gotcha), the DLR is using the runtime type of the row to resolve the member accesses. The groupings are implemented using internal classes and therefore the DLR cannot resolve the properties. The fact of the matter is, you _need_ to know what the type of the key is in order to use the interfaces. Otherwise you can fall back to using good ol' reflection. – Jeff Mercado Sep 25 '14 at 16:56