解析通用lambda表达式类型

时间:2015-06-10 22:17:26

标签: c# linq entity-framework

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

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),我有一个Action方法从客户端获取order参数。

这就是方法:

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上有很多关于它的信息,但是(参见action方法)变量必须在进程之前输入。无法在每个“case”中调用GetAll方法,因为它返回匿名类型。

我无法将所有列转换为Expression<Func<User, Object>>,因为实体框架不支持它。

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

1 个答案:

答案 0 :(得分:0)

您可以按如下方式重载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);