IQueryable <t> .OrderBy <t,object =“”> throws:无法将类型'System.Int16'强制转换为'System.Object'

时间:2015-07-06 08:26:20

标签: c# linq entity-framework generics

我们正在使用GenericCriteria类,该类用于查询IQueryable而无需直接访问它。基本上,查询是在“业务”层中构建的,并传递给“数据访问”层。为了防止重复,它需要尽可能通用。

以下是GenericCriteria类的简化版本:

public class GenericCriteria<T>
{
    public Expression<Func<T, bool>> Where { get; set; }
    public Expression<Func<T, object>> OrderBy { get; set; }
}

通过IQueryable上的扩展方法解决标准:

public static IQueryable<T> ResolveCriteria<T>(this IQueryable<T> query, GenericCriteria<T> criteria)
{
    query = query.Where(criteria.Where);
    query = query.OrderBy(criteria.OrderBy);
    return query;
}

这是它在数据层中的解决方法:

public ICollection<Person> GetPersons(GenericCriteria<Person> criteria)
{
    using (var context = new EFContext())
    {
        return context.Persons
            .AsQueryable()
            .ResolveCriteria(criteria)
            .ToList();
    }
}

其用法示例:

var criteria = new GenericCriteria<Person>();
criteria.Where = c => c.Age > 20;
criteria.OrderBy = c => c.Age;

var persons = Data.Instance.GetPersons(criteria);

问题是Entity Framework无法处理OrderBy表达式的 object 类型,抛出异常:

Unable to cast the type 'System.Int16' to type 'System.Object'

所以我现在要弄清楚的是如何让GenericCriteria类接受某种通用的OrderBy结构,以便EF能够做到这一点。

我应该寻找什么?

更新

如果不清楚,我宁愿避免:

public class GenericCriteria<T, TKey>
{
    public Expression<Func<T, bool>> Where { get; set; }
    public Expression<Func<T, TKey>> OrderBy { get; set; }
}

因为类的用户可能并不总是想订购,所以要求类型看起来有点脏。

2 个答案:

答案 0 :(得分:1)

我将重写Criteria,如下所示,

public class GenericCriteria<T>
{
     public List<Func<IQueryable<T>,IQueryable<T>>> 
          List { get; private set;}

     public GenericCriteria<T>(){
         List = new List<Func<IQueryable<T>,IQueryable<T>>>();
     }

}

解决

public static IQueryable<T> ResolveCriteria<T>(this IQueryable<T> 
       query, GenericCriteria<T> criteria)
{
    foreach(var exp in criteria.List){
       query = exp(query);
    }
    return query;
}

用法

var criteria = new GenericCriteria<Person>();
criteria.List.Add( q => q.Where( c => c.Age > 20) );
criteria.List.Add( q => q.OrderBy( c => c.Age ));

var persons = Data.Instance.GetPersons(criteria);

答案 1 :(得分:1)

由于您不希望GenericCriteria有两个类型参数,而OrderBy坚持使用两个类型参数,可能最好的方法是在ResolveCriteria中执行一些脏工作,如下:

public static IQueryable<T> ResolveCriteria<T>(this IQueryable<T> query, GenericCriteria<T> criteria)
{
  query = query.Where(criteria.Where);

  var t = criteria.OrderBy;
  var b = t.Body;
  if (b.NodeType == ExpressionType.Convert &&
      ((UnaryExpression)b).Type == typeof(Object)) {
    // Handle simple types, such as short, int, long, etc.
    var bb = ((UnaryExpression)b).Operand;
    var tt = Expression.Lambda(bb, t.Parameters);
    if (bb.Type == typeof(short))
      query = query.OrderBy((Expression<Func<T, short>>)tt);
    else if (bb.Type == typeof(int))
      query = query.OrderBy((Expression<Func<T, int>>)tt);
    else if (...)
      ...
  } else
    // Handle non-simple types, such as string.
    query = query.OrderBy(t);

  return query;
}

每种简单类型都需要“else if”;幸运的是,并不多。有关简单类型的列表,请参阅C# Simple Types