Linq要sql避免在哪里

时间:2015-08-21 14:16:43

标签: c# linq-to-sql

假设我有一个由我的程序中的其他部分传递的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));

1 个答案:

答案 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语句