C# 无论应用了多少变换,我如何在IQueryable中截取元素?

C# 无论应用了多少变换,我如何在IQueryable中截取元素?,c#,linq,C#,Linq,我需要包装一个IQueryable,这样当最终迭代结果时,我就可以访问返回的每个元素 为了实现这一点,我尝试创建一个QueryableWrapper类,该类充当真实queryable实例的中间人 然后,当我的存储库被要求提供它的IQueryable时,我返回了这个包装。在使用者应用任何进一步的LINQ语句(如Where等)之前,这都可以正常工作 我需要做的是让我的存储库在消费者迭代查询时拦截查询返回的每个项目。这样我就可以做以下事情了 结果是域对象吗 如果是,则跟踪对象以防修改 我还通过包装提供

我需要包装一个
IQueryable
,这样当最终迭代结果时,我就可以访问返回的每个元素

为了实现这一点,我尝试创建一个
QueryableWrapper
类,该类充当真实queryable实例的中间人

然后,当我的存储库被要求提供它的
IQueryable
时,我返回了这个包装。在使用者应用任何进一步的LINQ语句(如
Where
等)之前,这都可以正常工作

我需要做的是让我的存储库在消费者迭代查询时拦截查询返回的每个项目。这样我就可以做以下事情了

  • 结果是域对象吗
  • 如果是,则跟踪对象以防修改
  • 我还通过包装
    提供者的结果
    ,取得了一些进展,但当消费者执行
    OrderBy
    时,这会崩溃,因为我的结果不是
    IORDerenumerable

    我可以实现这一点,但我担心我会陷入困境,应该有更简单的方法来实现这一点

        internal class QueryableWrapper<T> : IQueryable<T>
        {
            private readonly IQueryable<T> Source;
            private readonly IQueryProvider QueryProvider;
    
            public QueryableWrapper(IQueryable<T> source)
            {
                CollectionName = collectionName;
                DbContext = dbContext;
                Source = source;
                QueryProvider = new QueryProviderWrapper<T>(source.Provider);
            }
    
            public Type ElementType => Source.ElementType;
    
            public Expression Expression => Source.Expression;
    
            public IQueryProvider Provider => QueryProvider;
    
            public IEnumerator<T> GetEnumerator() =>
                new EnumeratorWrapper<T>(Source.GetEnumerator());
    
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
    
    
        internal class EnumeratorWrapper<T> : IEnumerator<T>
        {
            private readonly IEnumerator<T> Source;
    
            public EnumeratorWrapper(IEnumerator<T> source)
            {
                Source = source;
            }
    
            public T Current => Source.Current;
    
            object? IEnumerator.Current => Current;
    
            public void Dispose()
            {
                Source.Dispose();
            }
    
            public bool MoveNext()
            {
                bool result = Source.MoveNext();
                if (result)
                {
                    Console.WriteLine("Intercepted " + Source.Current.GetType().FullName);
                }
                return result;
            }
    
            public void Reset()
            {
                Source.Reset();
            }
        }
    
        internal class QueryProviderWrapper<T> : IQueryProvider
        {
            private readonly IQueryProvider Source;
    
            public QueryProviderWrapper(IQueryProvider source)
            {
                Source = source;
            }
    
            public IQueryable CreateQuery(Expression expression) => CreateQuery<T>(expression);
    
            public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            {
                IQueryable<TElement> result = Source.CreateQuery<TElement>(expression);
                return new QueryableWrapper<TElement>(source: result);
            }
    
            public object? Execute(Expression expression) => Execute<T>(expression);
    
            public TResult Execute<TResult>(Expression expression) => Source.Execute<TResult>(expression);
        }
    }
    
    内部类QueryableWrapper:IQueryable
    {
    私有只读可读取源;
    专用只读IQueryProvider QueryProvider;
    公共QueryableWrapper(IQueryable源)
    {
    CollectionName=CollectionName;
    DbContext=DbContext;
    来源=来源;
    QueryProvider=新的QueryProviderWrapper(source.Provider);
    }
    公共类型ElementType=>Source.ElementType;
    public Expression=>Source.Expression;
    公共IQueryProvider=>QueryProvider;
    公共IEnumerator GetEnumerator()=>
    新的EnumeratorWrapper(Source.GetEnumerator());
    IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();
    }
    内部类枚举器包装器:IEnumerator
    {
    私有只读IEnumerator源;
    公共枚举器包装器(IEnumerator源)
    {
    来源=来源;
    }
    公共T Current=>Source.Current;
    对象?IEnumerator.Current=>Current;
    公共空间处置()
    {
    Source.Dispose();
    }
    公共图书馆
    {
    bool result=Source.MoveNext();
    如果(结果)
    {
    Console.WriteLine(“截获”+Source.Current.GetType().FullName);
    }
    返回结果;
    }
    公共无效重置()
    {
    Source.Reset();
    }
    }
    内部类QueryProviderWrapper:IQueryProvider
    {
    私有只读IQueryProvider源;
    公共QueryProviderWrapper(IQueryProvider源)
    {
    来源=来源;
    }
    公共IQueryable CreateQuery(表达式)=>CreateQuery(表达式);
    公共IQueryable CreateQuery(表达式)
    {
    IQueryable result=Source.CreateQuery(表达式);
    返回新的QueryableWrapper(来源:result);
    }
    公共对象?执行(表达式)=>执行(表达式);
    public TResult Execute(表达式)=>Source.Execute(表达式);
    }
    }
    
    内部类QueryableWrapper:IQueryable
    {
    私有只读可读取源;
    公共QueryableWrapper(IQueryable源)
    {
    来源=来源;
    }
    公共类型ElementType=>Source.ElementType;
    public Expression=>Source.Expression;
    公共IQueryProvider=>Source.Provider;
    公共IEnumerator GetEnumerator()=>
    新的EnumeratorWrapper(Source.GetEnumerator());
    IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();
    }
    内部类枚举器包装器:IEnumerator
    {
    私有只读IEnumerator源;
    公共枚举器包装器(IEnumerator源)
    {
    来源=来源;
    }
    公共T Current=>Source.Current;
    对象?IEnumerator.Current=>Source.Current;
    公共空间处置()
    {
    Source.Dispose();
    }
    公共图书馆
    {
    bool result=Source.MoveNext();
    如果(结果)
    控制台。WriteLine(“截获!”);
    返回结果;
    }
    公共无效重置()
    {
    Source.Reset();
    }
    }
    
    这只是因为
    queryable中的orderBy.orderBy
    将返回一个未包装的新
    IQueryable
    。新的
    MoveNext
    来自新函数提供的新实例

    在您的foreach中,使用的是未包装的

    试试这个:

    var data = new List<string> { "A", "B" };
    var queryable = new QueryableWrapper<string>(data.AsQueryable().OrderBy(x => x));
    foreach (string datum in queryable)
        Console.WriteLine("Ordered datum = " + datum);
    
    var data=新列表{“A”,“B”};
    var queryable=newqueryablewrapper(data.AsQueryable().OrderBy(x=>x));
    foreach(可查询的字符串数据)
    Console.WriteLine(“有序数据=”+数据);
    
    创建完整的可查询表达式,然后围绕最终结果创建包装器,让它截取它

    文件对此进行了解释:

    用户创建LINQ查询后,它将转换为命令树。命令树是与实体框架兼容的查询的表示形式。然后对数据源执行命令树

    在什么时候执行查询表达式可能会有所不同。LINQ查询总是在迭代查询变量时执行,而不是在创建查询变量时执行。这称为延迟执行。您还可以强制立即执行查询,这对于缓存查询结果很有用。本主题后面将对此进行描述


    这只是因为
    queryable.orderBy
    中的orderBy将返回一个未包装的新
    IQueryable
    。新的
    MoveNext
    来自新函数提供的新实例

    在您的foreach中,使用的是未包装的

    试试这个:

    var data = new List<string> { "A", "B" };
    var queryable = new QueryableWrapper<string>(data.AsQueryable().OrderBy(x => x));
    foreach (string datum in queryable)
        Console.WriteLine("Ordered datum = " + datum);
    
    var数据=新列表{
    
    namespace DomainDrivenDesign.MongoDB.Persistence
    {
        internal class QueryableWrapper<T> : IQueryable<T>, IOrderedQueryable<T>
        {
            private readonly IQueryable<T> Source;
            private readonly Lazy<IQueryProvider> QueryProvider;
    
            public QueryableWrapper(IQueryable<T> source)
            {
                Source = source;
                QueryProvider = new Lazy<IQueryProvider>(() =>
                    new QueryProviderWrapper<T>(collectionName, dbContext, source.Provider));
            }
    
            public Type ElementType => Source.ElementType;
    
            public Expression Expression => Source.Expression;
    
            public IQueryProvider Provider => QueryProvider.Value;
    
            public IEnumerator<T> GetEnumerator() => new EnumeratorWrapper<T>(Source.GetEnumerator());
    
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
    
    
        internal class EnumeratorWrapper<T> : IEnumerator<T>
        {
            private readonly IEnumerator<T> Source;
    
            public EnumeratorWrapper(IEnumerator<T> source)
            {
                CollectionName = collectionName;
                DbContext = dbContext;
                Source = source;
            }
    
            public T Current => Source.Current;
    
            object? IEnumerator.Current => Current;
    
            public void Dispose()
            {
                Source.Dispose();
            }
    
            public bool MoveNext()
            {
                bool result = Source.MoveNext();
                if (result)
                {
                    Console.WriteLine("Intercepted!");
                }
                return result;
            }
    
            public void Reset()
            {
                Source.Reset();
            }
        }
    
        internal class QueryProviderWrapper<T> : IQueryProvider
        {
            private readonly IQueryProvider Source;
    
            public QueryProviderWrapper(IQueryProvider source)
            {
                Source = source;
            }
    
            public IQueryable CreateQuery(Expression expression) => CreateQuery<T>(expression);
    
            public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            {
                IQueryable<TElement> result = Source.CreateQuery<TElement>(expression);
                return new QueryableWrapper<TElement>(result);
            }
    
            public object? Execute(Expression expression) => Execute<T>(expression);
    
            // Note that this bypasses Enumerator if the query ends with a scalar
            // such as .First()
            public TResult Execute<TResult>(Expression expression) => Source.Execute<TResult>(expression);
        }
    }