首页 > 解决方案 > 如何使用表达式选择组中的第一行

问题描述

当同一张表用于处理具有相似内容的不同数据时,我有一项任务。我使用动态 linq 表达式从 sql 获取数据。对我来说最复杂的任务是根据一个字段选择不同的行。表之一是零件,我将其作为示例显示。

我需要从 db 中选择每个描述的第一部分。Sql请求是:

SELECT * FROM [Parts] WHERE [Id] IN (SELECT MIN([Id]) FROM[Parts] GROUP BY [Description])

LINQ 请求是:

queryable = queryable.GroupBy(r => r.Dewcription).Select(g => g.FirstOrDefault());

我应该将 LINQ 查询重写为表达式。我已经成功使用 GROUP BY: var grouped = queryable.GroupBy(SelectedField, type); // 使用

public static IQueryable GroupBy([NotNull] this IQueryable source, string Property, [NotNull] Type type)
    {
        PropertyInfo property = type.GetProperty(Property);
        ParameterExpression parameter = Expression.Parameter(type, "r");
        MemberExpression member = Expression.MakeMemberAccess(parameter, property);
        LambdaExpression lambda = Expression.Lambda(member, parameter);
        MethodCallExpression resultExpression = GroupByCall(source, lambda, property.PropertyType, type);
        return source.Provider.CreateQuery(resultExpression);
    }

// 按方法应用分组

public static MethodCallExpression GroupByCall([NotNull] IQueryable queryable, LambdaExpression predicate, Type typeKey, Type typeSource)
    {
        MethodInfo MethodGroupBy = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
            .First(m => m.Name.Equals("GroupBy", StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == 2).MakeGenericMethod(typeSource, typeKey);
        Type QueryableType = typeSource.QueryableType();
        return Expression.Call(null, MethodGroupBy, Expression.Convert(queryable.Expression, QueryableType), Expression.Quote(predicate));
    }

// 获取要从 Queryable 转换的 Queryable 类型

public static Type QueryableType(this Type type)
    {
        var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
        MethodInfo Method = typeof(Queryable).GetMethod(nameof(Queryable.AsQueryable), new[] { typeof(IEnumerable) });
        var listAsQueryable = Method.Invoke(null, new[] { list });
        return listAsQueryable.GetType();
    }

下一步是为 .Select(g => g.FirstOrDefault()) 制作表达式。不能直接应用,因为分组实体的类型是未知的,分组的结果是IGrouping<string, object>,其中object实际上是我们在执行时知道的类型。我需要类似的东西:

var grouped = queryable.GroupBy(SelectedField, type).SelectGroupFirst(type); 

public static IQueryable SelectGroupFirst([NotNull] this IQueryable source, [NotNull] Type type)
{
    ParameterExpression parameter = Expression.Parameter(type, "r");
    MemberExpression member = Expression.MakeMemberAccess(parameter, ...get enumerable...);
    ...apply First() or FirstOrDefault()...
    LambdaExpression lambda = Expression.Lambda(member, parameter);
    MethodCallExpression resultExpression = SelectCall(source, lambda, ...IGrouping<string, type>..., type);
    return source.Provider.CreateQuery(resultExpression);
}

public static MethodCallExpression SelectCall(IQueryable queryable, LambdaExpression predicate, Type typeKey, Type typeSource)
{
    if (queryable == null) return null;
    var methods = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(m => m.Name.Equals("Select", StringComparison.OrdinalIgnoreCase)); 
    MethodInfo MethodSelect = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
        .First(m => m.Name.Equals("Select", StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == 2).MakeGenericMethod(typeSource, typeKey);
    return Expression.Call(null, MethodSelect, queryable.Expression, Expression.Quote(predicate));
}

我还没有找到动态进行 IGrouping<string, type> 类型定义、定义组枚举属性并访问它的方法。

替代方案可能是混合使用 SQL 和 LINQ 的方式。有没有办法将上述 sql 请求用于第一次选择行,并使用 LINQ 对其进行修改以进行分页和过滤?我还没有找到。

标签: c#linqgroup-bylinq-expressions

解决方案


推荐阅读