Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performing part of a IQueryable query and deferring the rest to Linq for Objects

I have a Linq provider that sucessfully goes and gets data from my chosen datasource, but what I would like to do now that I have my filtered resultset, is allow Linq to Objects to process the rest of the Expression tree (for things like Joins, projection etc)

My thought was that I could just replace the expression constant that contains my IQueryProvider with the result-sets IEnumerable via an ExpressionVisitor and then return that new expression. Also return the IEnumerable's provider from my IQueryable...but this does not seem to work :-(

Any idea's?

Edit: Some good answers here, but given the form...

var qry = from c in MyProv.Table<Customer>()
          Join o in MyProv.Table<Order>() on c.OrderID equals o.ID
          select new 
          {
            CustID = c.ID,
            OrderID = o.ID
          }

In my provider I can easily get back the 2 resultsets from Customers and Orders, if the data was from a SQL source I would just construct and pass on the SQL Join syntax, but it this case the data is not from a SQL source so I need to do the join in code...but as I said I have the 2 result sets, and Linq to Objects can do a join...(and later the projection) it would be real nice to just substitute the Expression constants MyProv.Table<Customer> and MyProv.Table<Order> with List<Customer> and List<Order> and let a List<> provider process the expression...is that possible? how?

like image 720
Tim Jarvis Avatar asked Mar 30 '09 00:03

Tim Jarvis


2 Answers

Both of the previous answers work, but it reads better if you use AsEnumerable() to cast the IQueryable to IEnumerable:

// Using Bob's code...
var result = datacontext.Table
   .Where(x => x.Prop == val)
   .OrderBy(x => x.Prop2)
   .AsEnumerable()  //  <---- anything after this is done by LINQ to Objects
   .Select(x => new { CoolProperty = x.Prop, OtherProperty = x.Prop2 });

EDIT:

// ... or MichaelGG's
var res = dc.Foos
           .Where(x => x.Bla > 0)  // uses IQueryable provider
           .AsEnumerable()
           .Where(y => y.Snag > 0); // IEnumerable, uses LINQ to Objects
like image 137
Lucas Avatar answered Oct 17 '22 08:10

Lucas


The kind of thing that I was after was replacing the Queryable<> constant in the expression tree with a concrete IEnumerable (or IQueryable via .AsQueryable()) result set...this is a complex topic that probably only makes any sense to Linq Provider writers who are knee deep in expression tree visitors etc.

I found a snippet on the msdn walkthrough that does something like what I am after, this gives me a way forward...

using System;
using System.Linq;
using System.Linq.Expressions;

namespace LinqToTerraServerProvider
{
    internal class ExpressionTreeModifier : ExpressionVisitor
    {
        private IQueryable<Place> queryablePlaces;

        internal ExpressionTreeModifier(IQueryable<Place> places)
        {
            this.queryablePlaces = places;
        }

        internal Expression CopyAndModify(Expression expression)
        {
            return this.Visit(expression);
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            // Replace the constant QueryableTerraServerData arg with the queryable Place collection.
            if (c.Type == typeof(QueryableTerraServerData<Place>))
                return Expression.Constant(this.queryablePlaces);
            else
                return c;
        }
    }
}
like image 38
Tim Jarvis Avatar answered Oct 17 '22 09:10

Tim Jarvis