实体框架查询的问题

时间:2010-07-22 01:51:49

标签: c# entity-framework

我写了一个类,它有一些查询。注意,whereitem是一个小类,它的Body成员包含一个Expression<Fun<T,bool>> lambda表达式用于过滤查询

public override List<T> Find<T>(int start, int len = 10, IWhereItem<T> whereitem = null, IOrderItem<T> orders = null, bool isDetach = true)
{
  var context = getInstence();
  var edminfo = EdmMgr[typeof (T)];//EdmMgr Include some dataservice infomation
  ObjectQuery<T> query = context.CreateQuery<T>("[" + edminfo.TableName + "]");
  if (whereitem != null && whereitem.Body != null)
    query = query.Where(whereitem.Body).AsObjectQuery();
  string orderString = orders == null ? "it." + edminfo.KeyName + " asc" : orders.OrderString;
  List<T> result = start == -1
             ? query.OrderBy(orderString).ToList()
             : query.OrderBy(orderString).Skip(start).Take(len).ToList();
  //......and else
  return result;
}

然后,当我运行它时,我收到一个错误,它说“LINQ to Entities查询不支持查询构建器方法。有关更多信息,请参阅实体框架文档。”

所以我改变了我的代码。

public override List<T> Find<T>(int start, int len = 10, IWhereItem<T> whereitem = null, IOrderItem<T> orders = null, bool isDetach = true)
{
  var context = getInstence();
  var edminfo = EdmMgr[typeof (T)];
  ObjectQuery<T> query = context.CreateQuery<T>("[" + edminfo.TableName + "]");
  query = MemerVisitor.IncludeMembers.Aggregate(query, (current, str) => current.Include(str));
  string orderString = orders == null ? "it." + edminfo.KeyName + " asc" : orders.OrderString;
  List<T> result = query.OrderBy(orderString).Skip(start).Where(whereitem.Body).Take(len).ToList();
  return result;
}

如此。没关系,但是很难看,对吗? OREREBY之后的WHERE。当我改变他们的位置时,我得到一个错误。类型不匹配。还有什么好方法?

==================附加============================ ==

为了便于接收调用,该方法在OrderItem排序语句上传递字符串(例如“it.UserId desc”),实际上WhereItem是Expression&gt;这个包,我的问题不是是否实现过滤器是entityframework语句,而是可以拆分链操作,因为如果第一次执行其中的查询后他转换为IQueryObject而不是ObjectQuery类型,那么一旦执行,执行后订单错误。 如:

      using (DataService<User> db = new DataService<User>())
            {
                user = db.Find(x => x.Moreinfo.CopSchool.CopSchoolId == 13&& x.Role.Title.Equals("xxxx")).FirstOrDefault();
            }

我把这个课包起来的DataService。相当于DAO。但是实现了EF。 有些查询可以传递,但有些查询不能传递。原因尚不清楚。但肯定是因为类型转换造成的。我正在使用。 Net 3.5sp1 vs2010默认实体数据模型(。Edmx)

如果我直接执行query.OrderBy(xxx)。跳过(xx)。其中(xxx)。拿(xx)。 ToList()返回结果,但是有一些问题,他是跳过最后xx个月的第一个订单过滤器。

我想查询。哪里(XX)。 OrderBy(xxx)。跳过(xx)。拿(xx)。 ToList()...但是无法执行因为Where(Expression)返回而不是ObjecteQuery IEnumerable(参数类型表达式的时间)或IQueryable(参数类型为Func时间)。哪里只能放在后面,而在他们有OrderBy之后跳过来打电话,在中间尴尬......

2 个答案:

答案 0 :(得分:1)

以下是我认为正在发生的事情:

您不能将.Where(whereitem.Body)应用于查询,因为它包含一个自定义函数表达式,实体框架不知道如何将其转换为SQL以在数据库上执行。

当你把.Where(whereitem.Body)放在.OrderBy(orderString).Skip(start)之后它工作正常,因为调用.OrderBy(orderString).Skip(start)导致sql执行并返回在内存中IEnumerable,您现在可以在内存集合中执行.Where(whereitem.Body)。它不再需要将该表达式转换为sql,而是将其作为CLR执行。你可以在Where可以转换为SQL的地方使用一个简单的lambda表达式,或者你可以通过执行query.AsEnumerable()来强制sql先评估。其中(...)ofcourse这将从db加载更多的结果在执行过滤之前进入内存。

另外,跳过和拍摄后过滤会得到与先过滤不同的结果。

第二个想法,你真正需要的是:

Find<T>(..., Func<TEntity,bool> predicate, ...)
{
...
query.Where(predicate)...


}

你需要将lambda表达式作为简单谓词传递,我相信应该让它可以转换为SQL。 (当然,假设谓词由简单的表达式组成,这些表达式本身可以通过EF转换为sql。)

答案 1 :(得分:0)

我找到了方法。

http://www.codeproject.com/KB/linq/IEnumerableSortByName.aspx?msg=3005452#xx3005452xx

我们可以使用extend方法SortEngine

private static IOrderedEnumerable<T> SortEngine<T>(this IEnumerable<T> source, string columnName, bool isAscending, bool started)
{
    var item = Expression.Parameter(typeof(T), "item");
    var propertyValue = Expression.PropertyOrField(item, columnName);
    var propertyLambda = Expression.Lambda(propertyValue, item);
    // item => item.{columnName}

    var sourceExpression = Expression.Parameter(typeof(IEnumerable<T>), "source");

    string methodName;
    Expression inputExpression;
    if (started)
    {
        methodName = isAscending ? "ThenBy" : "ThenByDescending";
        inputExpression = Expression.Convert(sourceExpression, typeof(IOrderedEnumerable<T>));
        // ThenBy requires input to be IOrderedEnumerable<T>
    }
    else
    {
        methodName = isAscending ? "OrderBy" : "OrderByDescending";
        inputExpression = sourceExpression;
    }

    var sortTypeParameters = new Type[] { typeof(T), propertyValue.Type };
    var sortExpression = Expression.Call(typeof(Enumerable), methodName, sortTypeParameters, inputExpression, propertyLambda);
    var sortLambda = Expression.Lambda<Func<IEnumerable<T>, IOrderedEnumerable<T>>>(sortExpression, sourceExpression);
    // source => Enumerable.OrderBy<T, TKey>(source, item => item.{columnName})

    return sortLambda.Compile()(source);
}

public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string columnName)
{
    return SortEngine(source, columnName, true, false);
}

public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> source, string columnName)
{
    return SortEngine(source, columnName, false, false);
}

public static IOrderedEnumerable<T> ThenBy<T>(this IOrderedEnumerable<T> source, string columnName)
{
    return SortEngine(source, columnName, true, true);
}

public static IOrderedEnumerable<T> ThenByDescending<T>(this IOrderedEnumerable<T> source, string columnName)
{
    return SortEngine(source, columnName, false, true);
}