假设我有一个由我的程序中的其他部分传递的id列表。我需要在DB上执行linq-to-sql查询(以查找其他一些数据)。我想根据识别我的范围内的项目的ID来做。
如果id列表足够小,我会在linq表达式中使用“contains”。但事实并非如此。问题是生成的SQL使用“where in(id1,id2 ...)”子句,它变得太大了。
我的问题是:我怎么能避免这种情况?
当然我不想要内存查询,这很容易 - 我想让DB按性能原因完成工作:)
更新:示例linq:
//this is passed in my program from somewhere
int[] myList = new int[]{0,1,2,3,4}; is a list of ids.
//this linq-to-sql will end up in a "where in" clause
myDataTable.Where(a => myList.Contains(a.ID));
答案 0 :(得分:0)
我们使用此扩展程序:
public static IQueryable<T> WhereValueIn<T>(this IQueryable<T> query,
Expression<Func<T, long>> propertyAccessor, ICollection<long> valuesList)
{
return query.Where(PrepareContainsPredicate(propertyAccessor, valuesList));
}
private static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
Expression<Func<T1, T2>> first,
Expression<Func<T2, T3>> second)
{
var param = Expression.Parameter(typeof(T1), @"param");
var newFirst = new ReplaceVisitor(first.Parameters.First(), param).Visit(first.Body);
var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst).Visit(second.Body);
return Expression.Lambda<Func<T1, T3>>(newSecond, param);
}
public static Expression<Func<T, bool>> PrepareContainsPredicate<T>(Expression<Func<T, long>> idAccessor, ICollection<long> entityIds)
{
if (entityIds.Count == 1)
{
var cardId = entityIds.First();
return Combine(idAccessor, x => x == cardId);
}
if (entityIds.Count == 2)
{
var card1Id = entityIds.ElementAt(0);
var card2Id = entityIds.ElementAt(1);
return Combine(idAccessor, x => x == card1Id || x == card2Id);
}
if (entityIds.Count == 3)
{
var card1Id = entityIds.ElementAt(0);
var card2Id = entityIds.ElementAt(1);
var card3Id = entityIds.ElementAt(2);
return Combine(idAccessor, x => x == card1Id || x == card2Id || x == card3Id);
}
return Combine(idAccessor, x => entityIds.Contains(x));
}
private sealed class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression _from;
private readonly Expression _to;
public ReplaceVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
{
return node == _from ? _to : base.Visit(node);
}
}
并称之为:
var ids = new long[] {1L, 2L};
var result = query.WhereValueIn(o => o.Id, ids);
因此,如果你有小集合,LINQ将使用Id = 1 OR Id = 2
生成代码,否则它将生成IN
语句