【问题标题】:Modify a LINQ `InRange` extension method to act as `NotInRange`修改 LINQ `InRange` 扩展方法以充当 `NotInRange`
【发布时间】:2011-08-09 19:51:02
【问题描述】:

如何将此方法更改为NotInRange?它应该只返回谓词与提供的values 不匹配的项目。

更新
方法名,InRange有点误导,应该是WhereInRange (或类似),因为它不返回布尔值; NotInRange 应该是 WhereNotInRange

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApplication5 {
    /// SAMPLE USAGE
    class Program {
        static void Main(string[] args) {
            // get some ids to play with...
            string[] ids;
            using(var ctx = new DataClasses1DataContext()) {
                ids = ctx.Customers.Select(x => x.CustomerID)
                    .Take(100).ToArray();
            }

            // now do our fun select - using a deliberately small
            // batch size to prove it...
            using (var ctx = new DataClasses1DataContext()) {
                ctx.Log = Console.Out;
                foreach(var cust in ctx.Customers
                        .InRange(x => x.CustomerID, 5, ids)) {
                    Console.WriteLine(cust.CompanyName);
                }
            }
        }
    }

    /// THIS IS THE INTERESTING BIT
    public static class QueryableChunked {
        public static IEnumerable<T> InRange<T, TValue>(
                this IQueryable<T> source,
                Expression<Func<T, TValue>> selector,
                int blockSize,
                IEnumerable<TValue> values) {
            MethodInfo method = null;
            foreach(MethodInfo tmp in typeof(Enumerable).GetMethods(
                    BindingFlags.Public | BindingFlags.Static)) {
                if(tmp.Name == "Contains" && tmp.IsGenericMethodDefinition
                        && tmp.GetParameters().Length == 2) {
                    method = tmp.MakeGenericMethod(typeof (TValue));
                    break;
                }
            }
            if(method==null) throw new InvalidOperationException(
                "Unable to locate Contains");
            foreach(TValue[] block in values.GetBlocks(blockSize)) {
                var row = Expression.Parameter(typeof (T), "row");
                var member = Expression.Invoke(selector, row);
                var keys = Expression.Constant(block, typeof (TValue[]));
                var predicate = Expression.Call(method, keys, member);
                var lambda = Expression.Lambda<Func<T,bool>>(
                      predicate, row);
                foreach(T record in source.Where(lambda)) {
                    yield return record;
                }
            }
        }
        public static IEnumerable<T[]> GetBlocks<T>(
                this IEnumerable<T> source, int blockSize) {
            List<T> list = new List<T>(blockSize);
            foreach(T item in source) {
                list.Add(item);
                if(list.Count == blockSize) {
                    yield return list.ToArray();
                    list.Clear();
                }
            }
            if(list.Count > 0) {
                yield return list.ToArray();
            }
        }
    }
}

此方法已复制自 Marc Gravell 对 this question 的回答。

【问题讨论】:

  • 你能不能创建只返回 !InRange() 的方法,或者你是否需要更多的东西?
  • 关于此方法需要注意的重要一点是,它只返回一个可枚举的,而不是可查询的。

标签: c# linq


【解决方案1】:

我认为您需要将其添加到谓词中

var predicate = Expression.Not(Expression.Call(method, keys, member));

【讨论】:

  • 我认为这会起作用,但事实并非如此。我不完全确定,但我认为仅通过更改这一行,它会为通过分块值的每次迭代产生一个“不包含”结果。
  • 让我澄清一下评论:每次迭代都会产生一个 DUPLICATE 结果。
【解决方案2】:

不确定这是否是一个可能的解决方案,但如何:

public static IEnumerable<T> NotInRange<T,TValue)(this IQueryable<T> source, Expression<Func<T,TValue>> selector, int blockSize, IEnumerable<TValue> values) 
{ 
     return !source.InRange(selector, blockSize, values); 
}

【讨论】:

  • InRange 不返回 boolean 所以 NotInRange 也不应该。此外,您提供的代码无效并且包含一些拼写错误。我承认术语InRange 暗示一个布尔值,作者可能应该将其命名为WhereInRange
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
  • 2014-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
相关资源
最近更新 更多