Reputation: 213
I have following linq expression:
Func<Entity, object> groupQuery = item =>
new { a = item.Attributes["name"], item = item.Attributes["number"] };
var result = target.Collection.Entities.GroupBy(groupQuery).ToList();
But if i don't know, how much columns i will group (for example 3 instead of 2),and names of the Attributes stored in List Names, How should i change my groupQuery object? My first idea was to create dynamic object like this but it don't work
dynamic groupQuery= new ExpandoObject();
IDictionary<string, object> dictionary = (IDictionary<string, object>)groupQuery;
foreach (string str in Names)
{
dictionary.Add(str, str);
}
Upvotes: 5
Views: 1762
Reputation: 213
answer in question C# LINQ - How to build Group By clause dynamically
IEnumerable<string> columnsToGroupBy = new[] { Names.First()};
Names.RemoveAt(0);
Names.Aggregate(columnsToGroupBy, (current, query) => current.Concat(new[] {query}));
GroupQuery = r => new NTuple<object>(from column in columnsToGroupBy select r[column]);
///////
using System;
using System.Collections.Generic;
using System.Linq;
namespace WBTCB.AggregationService.Models.Helpers
{
public class NTuple<T> : IEquatable<NTuple<T>>
{
public NTuple(IEnumerable<T> values)
{
Values = values.ToArray();
}
public readonly T[] Values;
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
if (obj == null)
return false;
return Equals(obj as NTuple<T>);
}
public bool Equals(NTuple<T> other)
{
if (ReferenceEquals(this, other))
return true;
if (other == null)
return false;
var length = Values.Length;
if (length != other.Values.Length)
return false;
for (var i = 0; i < length; ++i)
if (!Equals(Values[i], other.Values[i]))
return false;
return true;
}
public override int GetHashCode()
{
return Values.Aggregate(17, (current, value) => current*37 + (!ReferenceEquals(value, null) ? value.GetHashCode() : 0));
}
}
}
Upvotes: 1
Reputation: 6238
Instead of returning an object from groupQuery you can return a string. This string will be constructed from properties of objects that you want to group. Depending on the configuration it can be generated in different ways i.e. based on different properties. Here is a code that shows an idea:
public class A
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public enum GroupByuMode
{
GroupBy1,
GroupBy2,
GroupBy3,
}
...
var list = new List<A>();
for (int i = 0; i < 10; ++i)
for (int j = 0; j < 10; ++j)
for (int k = 0; k < 10; ++k)
list.Add(new A { Property1 = i.ToString(), Property2 = j.ToString(), Property3 = k.ToString() });
var mode = GroupByuMode.GroupBy1;
Func<A, object> func = a =>
{
if (mode == GroupByuMode.GroupBy1)
return a.Property1;
else if (mode == GroupByuMode.GroupBy2)
return String.Format("{0}_{1}", a.Property1, a.Property2);
else if (mode == GroupByuMode.GroupBy3)
return String.Format("{0}_{1}_{2}", a.Property1, a.Property2, a.Property3);
return null;
};
var res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
mode = GroupByuMode.GroupBy2;
res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
It works witch LINQ to Objects as shown above. You have to check if it works with LINQ to Entities or another implementation of LINQ.
Upvotes: 2