[英]LINQ extension for optimising Skip() Take()
I have been applying the optimization from this blog: RimDev.io我一直在应用这个博客的优化: RimDev.io
context.Cars
.Where(x => context.Cars
.OrderBy(y => y.Id)
.Select(y => y.Id)
.Skip(50000)
.Take(1000)
.Contains(x.Id)).ToList();
I want to convert this into a general LINQ extension, however, I am not sure how to refer to x.Id in Contains.我想将其转换为通用 LINQ 扩展,但是,我不确定如何在包含中引用 x.Id。 It does not seem like something that you could pass as an expression but it does not then specifically reference an instance of x.
它似乎不能作为表达式传递,但它不会专门引用 x 的实例。
Update here goes in progress code:此处更新正在进行中的代码:
public static class LinqExtension {
public static IQueryable<TSource> SkipTake<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> selector, int skip, int take)
where TSource: class {
return source.Where(x=> source.OrderBy<TSource,TKey(selector)
.Select(selector)
.Skip(skip)
.Take(take)
.Contains( ??? ));
}
}
I think you have stepped into the world of Expression
tree building.我想你已经步入了
Expression
树构建的世界。 Your question inspired me to create some new helpers to make this easier in certain cases.你的问题激励我创建一些新的助手,以便在某些情况下更容易。
Here are some extension methods that help with Expression
tree manipulation and building:以下是一些有助于
Expression
树操作和构建的扩展方法:
public static class ExpressionExt {
public static Expression Contains(this Expression src, Expression item) => src.Call("Contains", item);
public static Expression Call(this Expression p1, string methodName, params Expression[] px) {
var tKey = p1.Type.GetGenericArguments()[0];
var containsMI = typeof(Queryable).MakeGenericMethod(methodName, px.Length + 1, tKey);
return Expression.Call(null, containsMI, px.Prepend(p1));
}
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static T Replace<T>(this T orig, Expression from, Expression to) where T : Expression => (T)new ReplaceVisitor(from, to).Visit(orig);
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
}
public static class TypeExt {
public static MethodInfo GetGenericMethod(this Type t, string methodName, int paramCount) =>
t.GetMethods().Where(mi => mi.Name == methodName && mi.IsGenericMethodDefinition && mi.GetParameters().Length == paramCount).Single();
public static MethodInfo MakeGenericMethod(this Type t, string methodName, int paramCount, params Type[] genericParameters) =>
t.GetGenericMethod(methodName, paramCount).MakeGenericMethod(genericParameters);
}
Now you can create your SkipTake
method - I am assuming the Contains
member selector parameter is always the same member as the selector
parameter.现在,你可以创建你
SkipTake
方法-我假设Contains
成员选择参数始终是同样的部件selector
参数。
public static class LinqExtension {
public static IQueryable<TSource> SkipTake<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> selector, int skip, int take)
where TSource : class {
// x
var xParm = Expression.Parameter(typeof(TSource), "x");
var qBase = source.OrderBy(selector)
.Select(selector)
.Skip(skip)
.Take(take);
// selector(x)
var outerSelector = selector.Body.Replace(selector.Parameters[0], xParm);
// source.OrderBy(selector).Select(selector).Skip(skip).Take(take).Contains(selector(x))
var whereBody = qBase.Expression.Contains(outerSelector);
// x => whereBody
var whereLambda = Expression.Lambda<Func<TSource,bool>>(whereBody, xParm);
return source.Where(whereLambda);
}
}
To create the method, you need to build the lambda for the Where
method manually.要创建该方法,您需要手动为
Where
方法构建 lambda。 Rather than build the qBase
Expression
tree manually, I let the compiler do that for me, then use the resulting Expression
.我没有手动构建
qBase
Expression
树,而是让编译器为我做这件事,然后使用生成的Expression
。 My Call
helper makes it easy to create extension methods that correspond to the Queryable
extension methods but work on Expression
trees though, of course, you could just use Call
directly for any Queryable
methods you need (but then a Constant
helper would be useful). My
Call
helper 可以轻松创建与Queryable
扩展方法相对应的扩展方法,但可以在Expression
树上工作,当然,您可以直接将Call
用于您需要的任何Queryable
方法(但是Constant
helper 会很有用)。
Once you've built the whereLambda
, you just pass it to Where
for the original source
.构建
whereLambda
,只需将其传递给Where
获取原始source
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.