在 db 站点上动态实现这一点的方法非常复杂,因为我们无法动态创建匿名类型。要替换它们,我建议创建一个类:
public class CustomTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
我们不能在这里使用元组,因为它没有默认构造函数。在 CustomTuple 类中,最多放置尽可能多的参数 T 和尽可能多的属性。如果您将在该类中定义 5 个属性,但对于查询,您将只使用 3 个,您只需将 3 个属性设置为适当的值,其余 2 个属性保持为空 - 查询仍然有效。或者,您可以在运行时使用 CodeDOM 动态生成适当的类。然后是查询逻辑:
Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
Expression.MemberInit(
body,
binding1);
Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
var list = context.Entities.GroupBy(exp).ToList();
}
以上代码按值属性对实体进行分组。 parameterTypes 可以在程序执行期间动态构建 - 这是用于 group by 中的键选择的匿名类型的属性类型列表。基于此,我们创建了适当的 CustomTuple 类型。然后我们在运行时动态创建 binding1 元素 - 每个属性一个,为分组键设置。在上面的示例中,我只创建了一个。通过使用 NewExpression 和 MemberBinding 表达式,我们可以使用 MemberInit 方法构建初始化表达式。最后,您从中构建 lambda 表达式并针对 db 执行它。