【问题标题】:Resolving generic lambda expression type解析通用 lambda 表达式类型
【发布时间】:2015-06-10 22:17:25
【问题描述】:

我有一个调用实体框架函数的方法。它负责发送“select 子句”、“order by 子句”和可能的包含(考虑到我正在使用延迟加载)。该方法如下所示:

public IEnumerable<TReturn> GetAll<TReturn, TOrderKey>(
    Expression<Func<TEntity, TReturn>> selectExp,
    Expression<Func<TEntity, TOrderKey>> orderbyExp,
    Boolean descending,
    params Expression<Func<TEntity, Object>>[] includeExps)
{
       var query = DbSet.AsQueryable();
       query = !descending ? query.OrderBy(orderByExp) : query.OrderByDescending(orderByExp);
       if (includeExps != null)
          query = includeExps.Aggregate(query, (current, exp) => current.Include(exp));
       return query.Select(selectExp).ToList();
}

当我打电话时:

_service.GetAll(i => new { i.Name}, i => i.Name, false, null);

效果很好!那就是生成的 SQL 正是我想要的。

但是,考虑到一个真实的场景(在我的例子中,我使用的是 asp.net mvc),我有一个从客户端获取 order 参数的 Action 方法。

就是方法:

public JsonResult GetAllUsers(string sortColumn, bool sortDescending)
{
    //sortColumn string must be translated in a Expression
    var users = _service.GetAll(i => new { i.Name, i.Email }, i => i.Name, sortDescending, null);
    //
    //
}

我的第一次尝试是为每一列创建一个表达式,如下所示:

public JsonResult GetAllUsers(string sortColumn, bool sortDescending)
    {
        //I don't what is the Type that I should put here
        //It can be anything, like: Expression<Func<User, String>>,
        //Expression<Func<User, Guid>>, Expression<Func<User, Int>>
        ?Type? orderExp;
        switch(sortColumn)
        {
             case "UserId":
                 //Expression<Func<User, Guid>> 
                 orderExp = i => i.UserId; 
                 break;
            case "Name":
                 //Expression<Func<User, String>> 
                 orderExp = i => i.Email; 
                 break;
        }
        //sortColumn string must be translated in a Expression
        var users = _service.GetAll(i => new { i.Name, i.Email }, orderExp, sortDescending, null);
        //
        //
    }

我想创建一个基于 sortProperty 的表达式,stackoverflow 上有很多关于它的信息,但是(请参阅操作方法)必须在处理之前键入变量。不能在每个“案例”中调用 GetAll 方法,因为它返回一个匿名类型。

我无法将所有列都转换为Expression&lt;Func&lt;User, Object&gt;&gt;,因为实体框架不支持它。

Linq.Dynamic 应该有帮助,但我不想使用字符串参数。

【问题讨论】:

  • 你的问题到底是什么?如果您不能在第一次尝试时将您的问题陈述为问题,那么您将被否决并关闭。
  • 对不起...我认为还不够清楚。我想根据属性名称创建一个表达式,stackoverflow 上有很多关于它的信息,但是(请参阅操作方法)必须在处理之前键入变量。 GetAll 返回一个匿名类型,应该在方法结束时调用。
  • 所以你想要一个谓词构建器,除了你想根据它排序和/或选择数据子集(有一个函数,而不是一个谓词)。考虑一下:stackoverflow.com/a/3291811/1144090 - 告诉我们不能按列号排序,这是我想将列名转换成的。或者,使用Telerik :)
  • 我可以使用 Func,但它们不会被翻译成 SQL。 Func 在数据加载到内存后运行。
  • 你可以在这里stackoverflow.com/a/18321266/1479335找到如何按列名排序。严格来说不是你想要的,但我想至少可以应用于排序。

标签: c# linq entity-framework


【解决方案1】:

您可以重载GetAll 方法,如下所示:

public IEnumerable<TReturn> GetAll<TReturn>(
    Expression<Func<TEntity, TReturn>> selectExp,
    string orderColumnName,
    Boolean descending,
    params Expression<Func<TEntity, Object>>[] includeExps)
{
    var entityType = typeof(TEntity);
    var prop = entityType.GetProperty(orderColumnName);
    var param = Expression.Parameter(entityType, "i");
    var orderExp = Expression.Lambda(
        Expression.MakeMemberAccess(param, prop), param);

    // get the original GetAll method overload
    var method = this.GetType().GetMethods().Where(m => m.Name == "GetAll" && m.GetGenericArguments().Length == 2);
    var actualMethod = method.First().MakeGenericMethod(typeof(TReturn), prop.PropertyType);
    return (IEnumerable<TReturn>)actualMethod.Invoke(this, new object[] { selectExp, orderExp, descending, includeExps });
}

只需为 null 值添加更多检查,否则无效的 sortColumn 名称会再次困扰您。

使用它类似于您当前的方法:

_service.GetAll(i => new { i.Name, i.Email }, sortColumn, sortDescending, null);

【讨论】:

    猜你喜欢
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    相关资源
    最近更新 更多