Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7997301
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 4, 20262026-06-04T14:56:24+00:00 2026-06-04T14:56:24+00:00

I have a simple custom QueryProvider that takes an expression, translates it to SQL

  • 0

I have a simple custom QueryProvider that takes an expression, translates it to SQL and queries an sql database.

I want to create a small cache in the QueryProvider that stores commonly accessed objects so retrieval can happen without a database hit.

The QueryProvider has the method

public object Execute(System.Linq.Expressions.Expression expression)
{
    /// Builds an SQL statement from the expression, 
    /// executes it and returns matching objects
}

The cache sits as a field in this QueryProvider class and is a simple generic List.

If I use the List.AsQueryable method and pass the above expression into the List.AsQueryable’s Provider’s Execute method it doesn’t work as desired. It looks like when an expression gets compiled the initial QueryProvider becomes an integral part.

Is it possible to pass an expression to a subsequent QueryProvider and execute the expression as desired?

The calling code looks vaguely as follows:

public class QueryProvider<Entity>()
{
    private List<TEntity> cache = new List<Entity>();

    public object Execute(System.Linq.Expressions.Expression expression)
    {
        /// check whether expression expects single or multiple result
        bool isSingle = true;

        if (isSingle)
        {
            var result = this.cache.AsQueryable<Entity>().Provider.Execute(expression);
            if (result != null) 
                return result;
        }

        /// cache failed, hit database
        var qt = new QueryTranslator();
        string sql = qt.Translate(expression);
        /// .... hit database
    }
} 

It doesn’t return an error, instead it gets stuck in loop where this same provider is called over and over again.

Here’s some more code showing what I’m trying to do:

Collection:

class Collection<Entity>
{

    internal List<Entity> cacheOne { get; private set; }
    internal Dictionary<Guid, Entity> cacheTwo { get; private set; }

    internal Collection()
    {
        this.cacheOne = new List<Entity>();
        this.cacheTwo = new Dictionary<Guid, Entity>();
    }

    public IQueryable<Entity> Query()
    {
        return new Query<Entity>(this.cacheOne, this.cacheTwo);
    }

}

Query:

class Query<Entity> : IQueryable<Entity>
{
    internal Query(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.Provider = new QueryProvider<Entity>(cacheOne, cacheTwo);
        this.Expression = Expression.Constant(this);
    }

    internal Query(IQueryProvider provider, Expression expression)
    {
        this.Provider = provider;
        if (expression != null)
            this.Expression = expression;
    }

    public IEnumerator<Entity> GetEnumerator()
    {
        return this.Provider.Execute<IEnumerator<Entity>>(this.Expression);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public Type ElementType
    {
        get { return typeof(Entity); }
    }

    public System.Linq.Expressions.Expression Expression { get; private set; }

    public IQueryProvider Provider { get; private set; }
}

QueryProvider:

class QueryProvider<Entity> : IQueryProvider
{

    private List<Entity> cacheOne;
    private Dictionary<Guid, Entity> cacheTwo;

    internal QueryProvider(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.cacheOne = cacheOne;
        this.cacheTwo = cacheTwo;   
    }

    public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
    {
        return new Query<TElement>(this, expression);
    }

    public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
    {
        throw new NotImplementedException();
    }

    public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
    {
        return (TResult)this.Execute(expression);
    }

    public object Execute(System.Linq.Expressions.Expression expression)
    {
        Iterator<Entity> iterator = new Iterator<Entity>(expression, cacheOne, cacheTwo);
        return (iterator as IEnumerable<Entity>).GetEnumerator();
    }
}

Iterator:

class Iterator<Entity> : IEnumerable<Entity>
{
    private Expression expression;
    private List<Entity> cacheOne;
    private Dictionary<Guid, Entity> cacheTwo;

    internal Iterator(Expression expression, List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.expression = expression;
        this.cacheOne = cacheOne;
        this.cacheTwo = cacheTwo;
    }

    public IEnumerator<Entity> GetEnumerator()
    {
        foreach (var result in (IEnumerable<Entity>)this.cacheOne.AsQueryable<Entity>().Provider.Execute(expression))
        {
            yield return result;
        }

        foreach (var more in (IEnumerable<Entity>)this.cacheTwo.Values.AsQueryable<Entity>().Provider.Execute(expression))
        {
            yield return more;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Program:

class Program
{
    static void Main(string[] args)
    {
        /// Create collection + caches
        var collection = new Collection<Giraffe>();
        collection.cacheOne.AddRange(new Giraffe[] {
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2011, 03, 21), Height = 192, Name = "Percy" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2005, 12, 25), Height = 188, Name = "Santa" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1999, 04, 01), Height=144, Name="Clown" }
        });
        var cachetwo = new List<Giraffe>(new Giraffe[] {
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1980, 03,03), Height = 599, Name="Big Ears" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1985, 04, 02), Height= 209, Name="Pug" }
        });
        foreach (var giraffe in cachetwo)
            collection.cacheTwo.Add(giraffe.Id, giraffe);

        /// Iterate through giraffes born before a certain date
        foreach (var result in collection.Query().Where(T => T.DateOfBirth < new DateTime(2006, 01, 01)))
        {
            Console.WriteLine(result.Name);
        }

    }
}

Giraffe:

class Giraffe
{
    public Guid Id { get; set; }
    public string Name { get; set;  }
    public long Height { get; set; }
    public DateTime DateOfBirth { get; set; }
}

Special cases e.g. SingleAndDefault, etc are left out. The part I’m wanting to work happens in Iterator, where it first of all executes the List’s QueryProvider before executing the Dictionary’s.

One of the two Queryable objects might be a database, or something else.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-04T14:56:26+00:00Added an answer on June 4, 2026 at 2:56 pm

    No, a query does not become bound to a provider. That’s why you have the IQueryable interface: it provides both the Expression and the Provider, so LINQ can call the provider to execute the expression.

    The problem in your implementation is in the way Query<Entity> represents itself: you’re setting the root expression to Expression.Constant(this), where this is the query (not the collection).

    So when you execute the query with LINQ-to-Objects, it will call GetEnumerator on Query<>, which then calls LINQ-to-Objects to execute Expression, which has a root expression Expression.Constant(this) (of type Query<>), and LINQ-to-Objects then iterates this root expression by calling GetEnumerator on this Query<>, etc.

    The problem lies in

    (IEnumerable<Entity>)this.cacheOne.AsQueryable<Entity>().Provider.Execute(expression)
    

    which is basically equal to

    new Entity[0].AsQueryable().Provider.Execute(expression)
    

    or

    linqToObjectsProvider.Execute(expression)
    

    The Provider returned by a query is not linked to the source (this.cacheOne), so you’re just re-executing the expression, not querying over your cache.

    What’s wrong with the following?

    class Collection<Entity>
    {
        ...
    
        public IQueryable<Entity> Query()
        {
            return this.cacheOne.Concat(this.cacheTwo.Values).AsQueryable();
        }
    }
    

    Note that Concat uses delayed evaluation, so only when you execute the query are cacheOne and cacheTwo concatenated and then manipulated using the additional LINQ operators.

    (In which case, I’d make Collection<Entity> an IQueryablewithExpressionequal toExpression.Constant(this.cacheOne.Concat(this.cacheTwo.Values))`. I think you can do away with all the other classes.)


    Original answer

    However, I don’t think this way of piggy-backing LINQ to Objects will ever be able to do what you think it should.

    At the very least, you should keep the original query provider so you can call that one when you have a cache miss. If you don’t, and use your own query provider (you did not show the code you are using to do the actual call), your query provider one will call itself again, and again.

    So you’ll need to create a CachingQueryProvider and a CachingQuery:

    class CachingQuery<T> : IQueryable<T>
    {
        private readonly CachingQueryProvider _provider;
        private readonly Expression _expression;
    
        public CachingQuery(CachingQueryProvider provider, Expression expression)
        {
            _provider = provider;
            _expression = expression;
        }
    
        // etc.
    }
    
    class CachingQueryProvider : IQueryProvider
    {
        private readonly IQueryProvider _original;
    
        public CachingQueryProvider(IQueryProvider original)
        {
            _original = original;
        }
    
        // etc.
    }
    
    public static class CachedQueryable
    {
        public static IQuerable<T> AsCached(this IQueryable<T> source)
        {
            return new CachingQuery<T>(
                 new CachingQueryProvider(source.Provider), 
                 source.Expression);
        }
    }
    

    Also if you want to cache a result, you’ll need to materialize the result before you cache it, otherwise you cache the query, not the result. And the result itself should never be executed again, as it is already the data you should return.

    The direction I would head into is as follows:

    class CachingQueryProvider : IQueryProvider
    {
        public object Execute(Expression expression)
        {
            var key = TranslateExpressionToCacheKey(expression);
    
            object cachedValue;
            if (_cache.TryGetValue(key, out cachedValue))
                return cachedValue;
    
            object result = _originalProvider.Execute(expression);
    
            // Won't compile because we don't know T at compile time
            IEnumerable<T> sequence = result as IEnumerable<T>;
            if (sequence != null && !(sequence is ICollection<T>)) 
            {
                result = sequence.ToList<T>();
            }
    
            _cache[key] = result; 
    
            return result;
        }
    }
    

    For the part marked as Won't compile, you’ll have to do some reflection trickery.

    And caution: string implements IEnumerable, so be careful not to try to materialize a single string result value.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a simple custom view that is connected via outlet to a NIB.
i'm missing something fundamental here. i have a very simple custom class that draws
I'm trying to create a simple, custom, gallery. I have two lists, one with
I have a simple custom UIView (a rectangle) that is implemented with drawRect. The
I have a simple usercontrol that uses a simple custom panel where I just
I have never seen this before. I have a simple custom view that has
I have the following simple custom control that I have defined for a Windows
I have a simple custom user control that uses jqGrid. the control is as
I have a simple custom error handler that writes in a error log file
I have a really simple custom animation that I am trying to run, and

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.