【问题标题】:Expression trees in middle of regular LINQ method chaining; dynamic and nullable types?常规 LINQ 方法链接中间的表达式树;动态和可空类型?
【发布时间】:2020-06-25 18:59:08
【问题描述】:

我正在尝试编写动态查询,但在处理查询的 OrderBy 部分中的动态类型时遇到了问题。我已经看到一些使用方法调用表达式的解决方案,这可能对我有用,但我不知道如何将其融入我现有的代码中,我在任一端都使用方法链接。我可以只使用 dynamic 作为我的类型,但我认为这不适用于 Nullable 类型。

public async Task<IEnumerable<MyEntityModel>> GetEntities(QueryEntityResource request)
{
    IQueryable<MyEntityModel> queryableData = ...;
    Expression whereLambdaExp = ...;

    queryableData = queryableData.Where(whereLambdaExp);

    ParameterExpression param = Expression.Parameter(typeof(MyEntityModel));
    PropertyInfo property = typeof(MyEntityModel).GetProperty(request.sortModel.ColId);
    Expression propExpr = Expression.Property(param, property);
    var lambdaExpression = Expression.Lambda<Func<MyEntityModel, dynamic>>(propExpr , new ParameterExpression[] { param }); // can't use dynamic

    // Errors here; can't use dynamic type in lambda expression -- Need to specify types
    if (request.sortModel.Sort == "asc")
    {
        queryableData = queryableData.OrderBy(lambdaExpression);
    }
    if (request.sortModel.Sort == "desc")
    {
        queryableData = queryableData.OrderByDescending(lambdaExpression);
    }

    queryableData = queryableData.Skip(request.StartRow).Take(request.EndRow - request.StartRow);

    return queryableData.ToListAsync();
}

如果我使用动态,它将不适用于我的可为空类型:

System.ArgumentException:“System.Nullable`1[System.Int32]”类型的表达式不能用于返回类型“System.Object”

【问题讨论】:

  • 仅供参考,Expression.Lambda 有一个params 变体,它将为您创建数组,因此您不需要new ParameterExpression[](并且,您不需要ParameterExpression,因为new[]可以从{}中的表达式推断出数组类型。
  • 是的,构建Expressions 以与 LINQ 查询运算符一起使用的问题是它向上传播(像病毒一样)。由于OrderBy 是通用的,因此您需要对OrderBy&lt;TSource,TKey&gt; 进行正确的方法调用并固定通用属性,因此您还必须将其构建为Expression 树并且因为queryableDataIQueryable&lt;T&gt;将需要替换 Expression
  • @NetMage 请给我一个代码示例以及它如何适合我现有的代码?

标签: c# linq


【解决方案1】:

这是未经测试的;但我认为它接近你所需要的(我觉得我以前写过这段代码......另外,见this answer):

您可以构建对OrderBy 的调用,实例化为您需要的类型,然后构建一个新的IQueryable。我的方法删除了async,因为我无法在 LINQPad 中为它构建快速测试。

public IEnumerable<MyEntityModel> GetEntities(QueryEntityResource request) {
    IQueryable<MyEntityModel> queryableData = default;
    Expression<Func<MyEntityModel, bool>> whereLambdaExp = default;

    queryableData = queryableData.Where(whereLambdaExp);

    var param = Expression.Parameter(typeof(MyEntityModel));
    var propExpr = Expression.Property(param, request.sortModel.ColId);
    var lambdaExpression = Expression.Lambda(propExpr, param);

    if (request.sortModel.Sort == "asc" || request.sortModel.Sort == "desc")
        queryableData = queryableData.Provider.CreateQuery<MyEntityModel>(Expression.Call(typeof(Queryable), request.sortModel.Sort == "asc" ? "OrderBy" : "OrderByDescending",
                        new[] { typeof(MyEntityModel), propExpr.Type }, queryableData.Expression, Expression.Quote(lambdaExpression)));

    queryableData = queryableData.Skip(request.StartRow).Take(request.EndRow - request.StartRow);

    return queryableData.ToList();
}

【讨论】:

  • @xb4rrm27m5 注意,在切换到更好的Expression.Call 重载后,我不再需要扩展方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
相关资源
最近更新 更多