using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static class DataReaderExtensions
public static IEnumerable<T> SelectRows<T>(this IDataReader reader, Func<IDataRecord, T> select)
yield return select(reader);
public static void Test()
var cmd = JsonConvert.DeserializeObject<DataTable>(GetInputJson());
using (IDataReader reader = cmd.ExecuteReader())
id = r.GetInt64(r.GetOrdinal("pid")),
name = r["contributor"].ToString(),
project = new {id = r.GetInt64(r.GetOrdinal("projectID")), name = r["projectName"].ToString() },
.GroupBy(r => new { r.id, r.name })
.Select(g => new { g.Key.id, g.Key.name, projects = g.Select(i => i.project) });
var json = JsonConvert.SerializeObject(new { contributors = query }, Formatting.Indented);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(GetRequiredJson())));
static string GetInputJson()
{'pid': 1, 'contributor': 'jdalton', 'projectID': 1, 'projectName': 'lodash'},
{'pid': 1, 'contributor': 'jdalton', 'projectID': 2, 'projectName': 'docdown'},
{'pid': 1, 'contributor': 'jdalton', 'projectID': 3, 'projectName': 'lodash-cli'},
{'pid': 2, 'contributor': 'contra', 'projectID': 4, 'projectName': 'gulp'},
{'pid': 3, 'contributor': 'phated', 'projectID': 4, 'projectName': 'gulp'},
static string GetRequiredJson()
static class TestExtensions
public static IDataReader ExecuteReader(this DataTable table)
return table.CreateDataReader();
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];