【问题标题】:How to get original collection from an IQueryable<T>如何从 IQueryable<T> 获取原始集合
【发布时间】:2012-10-03 19:53:44
【问题描述】:

考虑以下几点:

T[] itemArray = // initialized values
IQueryable<T> itemQuery = itemArray.AsQueryable().Where(*/some query*/).Skip(5).Etc() ...

是否可以从itemQuery 获取底层集合或数据集(在本例中为itemArray)?在我看来,如果在评估查询表达式时,必须根据原始集合对其进行评估,因此该集合必须存储在 IQueryable 中或由 IQueryable 引用。

如何做到这一点?

注意:
这是一个带有 MongoDB linq 驱动程序(来自 10gen)的更大项目的一部分,我们试图从基于 mongo 集合的 IQueryable 中提取原始 MongoCollection。虽然我们专门针对 MongoDB 使用 IQueryable,但这个问题的答案应该是 IQueryable 所固有的,因此独立于 MongoDB 驱动程序。

【问题讨论】:

    标签: c# linq iqueryable


    【解决方案1】:

    IQueryable 很可能确实引用了底层集合(可能通过多个间接层),但它不会公开,因此您至少无法访问它绝不是我认为合理,也不是非常非常混乱的黑客攻击。

    【讨论】:

    • +1 - 我相信 EF Linq 提供程序只是存储任何过滤器等以投射 SQL,因此可能无法通过 IQueryable 引用访问整个表。我希望 MongoDB 提供程序是相似的,并且不会过滤内存中的数据。
    【解决方案2】:

    不,每个查询都是独立执行的,剩下的就是查询本身的结果集。内存对象中没有任何内容包含原始集合,也没有对原始集合的任何可访问引用。考虑到原始集合现在可能为空或超出范围,即使允许这样做也是危险的。如果您需要原始集合,则需要将其保留在范围内,直到您完全完成它为止。

    【讨论】:

    • 嗯,原始集合必须存在于某处。这里的查询使用延迟执行,因此它只是定义如何从集合中提取数据,在您尝试枚举它之前它实际上并没有提取它。因此,它需要引用实际的集合(至少是间接的)。
    • @Servy:是的,我用“可访问”一词进行了编辑。出于执行目的,存在对原始集合的引用,但没有可访问的引用。我不是 100% 确定,但我相信一旦执行执行,这个引用就会被丢弃。不过,不要引用我的话。在我看来,我在某处读到了类似的东西(或者至少是让我相信的东西:)
    • 虽然自定义查询提供程序当然有可能做到这一点,但没有一个明智的人会这样做。对于初学者,您可以多次枚举 [大多数] 查询,因此故意丢弃枚举“您自己”所需的信息会阻止这种情况(标准提供者都没有这样做)。正如我在回答中所说,您可能无法(实际上)从IQueryable 中获取参考,但它就在其中(某处)。
    • @Servy:一旦执行发生,新数据就会被加载到单独的内存空间中。原始引用只需要足够长的时间就可以执行。虽然现在你已经激起了我的好奇心。我将不得不运行一些范围测试,以了解当源集合在执行之后和之前超出范围时延迟执行会发生什么。
    • 正如我所说,查询将在其整个生命周期中保留对源集合的引用,因此,根据定义,源集合在查询发生之前不能超出范围。一些引用实际集合(实际程序员可以访问)的变量可能会超出范围,但在可查询对象消失之前,底层集合(在本例中为数组)不能超出范围。如果确实如此(可能通过平均不安全代码),那么查询将中断,因为它将查询不再存在的数据。
    【解决方案3】:

    这是作弊,但在您的上下文中可能是有道理的:

    interface IReferencedQueryable<T> : IQueryable<T>
    {
        IEnumerable<T> Source { get; }
    }
    
    static class IReferencedQueryableExtensions
    {
        public static IReferencedQueryable<T> AsReferencedQueryable<T>(
            this IEnumerable<T> source)
        {
            return ReferencedQueryable.From(source);
        }
    
        class ReferencedQueryable<T> : IReferencedQueryable<T>
        {
            public IEnumerable<T> Source { get; private set; }
    
            ReferencedQueryable(IEnumerable<T> source)
            {
                Source = source;
            }
    
            static IReferencedQueryable<T> From(IEnumerable<T> source)
            {
                return new ReferencedQueryable(source);
            }
    
            // all the IQueryable members would be 
            // implemented through AsQueryable()
            // ...
        }
    
        public static IReferencedQueryable<T> Where<T>(
            this IReferencedQueryable<T> source, 
            Expression<Func<T, bool>> predicate)
        {
            return ReferencedQueryable.From(
                ((IQueryable<T>)source).Where(predicate));
        }
    
        // do the same for all the Linq extension methos you want to support
    }
    

    你会这样使用它:

    T[] itemArray = // initialized values
    var itemQuery = itemArray.AsReferencedQueryable()
                             .Where(*/some query*/)
                             .Skip(5)
                             .Etc() ...
    

    当您需要源序列时,您可以通过 Source 成员访问它,也可能通过 as 演员表访问它。

    var s = itemQuery.Source;
    

    【讨论】:

      猜你喜欢
      • 2020-12-22
      • 1970-01-01
      • 2018-12-26
      • 1970-01-01
      • 2021-05-15
      • 2016-09-28
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      相关资源
      最近更新 更多